TL;DR: # coding: unicode_escape
PyExec was a python-sandbox-escape type challenge on the recent 30C3 CTF. Basically you were allowed to execute filtered Python code server-side, where the filter was a mix of a blacklist and a whitelist. The solution turned out to be quite amusing :)
The code was fed to the server using a very simple web app that looked like this:
Since you were also given the source code of the server, you could peek at the filtering code, which worked like this:
- It blacklisted all interesting function names (strings search), like "open" or "eval".
- It allowed (whitelisted) only characters that matched this regular expression:
^[\r\na-z0-9#\t,+*/:%><= _\\\-]*$ The regexp allows:- whitespaces
- lower letters (only lower, no upper)
- digits
- and these special characters: # , + * / \ : % > < = _ -
Codes that passed the filter were written into a temporary file and executed by an external Python process.
The solution is obvious if you have played with Python source code charsets before - declare a coding using the # coding: name directive and encode your payload with it.
At first we tried to go with utf-7, but since no upper characters are allowed, that turned out to be a no go. Finally we settled for a Python-specific codec called unicode_escape which allows the whole source code to look like inside-a-string escape sequences:
# coding: unicode_escape
\x41\x41\x41\x41
In the end we've encoded a Python reverse shell to this encoding and used it to find and read the flag:
> nc -v -l 1234 Connection from 88.198.89.213 port 1234 [tcp/*] accepted /bin/sh: 0: can't access tty; job control turned off $ ls flag.txt static webapp.py $ cat flag.txt 30C3_2a2766d9cf4a137d517a8227bd71d59d $
And that's it.
Can you please give us the payload ?
ReplyDelete