Rating:

Pyjail

We were given the challenge text:

Escape me plz.

EU instance: 207.180.200.166 1024

US instance: 45.134.3.200 1024

author: pop_eax & Tango

Along with the file jailbreak.py.

I have never done a Python breakout challenge before, but this was extremely fun! We could execute Python commands, but very restriced. If our input had a banned word it would fail, and we also didn't have access to the Python __builtins__. After a while of reading up on how Python's exec worked, I did however realized that we had access to scope's global variables through the variable globals. If we executed the code print(globals), we got the globals dict as a response

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0204AF70>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'jailbreak.py', '__cached__': None, 're': <module 're' from 'C:\\Users\\andre\\AppData\\Local\\Programs\\Python\\Python38-32\\lib\\re.py'>, 'version': '3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:43:08) [MSC v.1926 32 bit (Intel)]', 'banned': 'import|chr|os|sys|system|builtin|exec|eval|subprocess|pty|popen|read|get_data', 'search_func': <function <lambda> at 0x02369E80>, 'main': <function main at 0x0239DDF0>}

I then realized that we could access the __builtins__ module through the globals variable by doing globals['__builtins__']. This would allow us to the function __import()__. The command I wished to execute was open('/flag.txt', 'r').read(). To use open() i had to import it, but import was in the banlist. I had a look at the __builtins__ once again again, and noticed getattr(). getattr() requires 2 parameters, the first one being an object and the second one must be a string. For example, getattr(x, 'foobar') is equivalent to x.foobar. The object argument can however also be a string, which allows us to do getattr('__imp'+'ort__', 'open'). So to import open, we have to use the code: globals['__builtins__'].getattr('__imp'+'ort__', 'open'). To get read() of open(), we use the same getattr trick. The final command did however not work, as the open() function returned an error due to the restrictions of the pyjail.

The new command I planned to execute was os.read(os.open('/flag.txt', os.O_RDONLY)). I once again used the getattr trick to bypass the bans for import and read. I also used lambdas to simulate variables. In the command b = globals['__builtins__'] and o = b.getattr(b, '__imp'+'ort__')('o'+'s')). I also encountered one final error, since the the Python script made my input lowercase, os.O_RDONLY would become lowercase and not work. Luckily this was just a flag, and I instead replaced it with 0. Here's the final command that prints the contents of /flag.txt, which was flag{l3t's_try_sc0p1ng_th1s_0ne_2390098}.

print((lambda b: (lambda o: b.getattr(o, 're'+'ad')(o.open('/flag.txt', 0), 64))(b.getattr(b, '__imp'+'ort__')('o'+'s')))(globals['__builtins__']))

Some more solutions

Afterwards I realized I could simply have used os.system('cat /flag.txt') to get the output, which also made it a bit shorter:

(lambda b: b.getattr(b.getattr(b, '__imp'+'ort__')('o'+'s'), 'sy'+'stem')('cat /f*'))(globals['__builtins__'])

After talking with the creator since I thought it was an incredibly fun challenge, I learned that his "intended" solution was to clear the string of banned characters. I then made this much cleaner and simpler 2 liner exploit:

globals['banned'] = 'a'
globals['__builtins__'].__import__('os').system('cat /f*')
Original writeup (https://github.com/WastefulNick/CTF-Writeups/tree/master/0x41414141/misc/pyjail).