How Fastjson’s AutoType Bypass Enables File Read and SSRF Attacks

This article provides a detailed analysis of the recent Fastjson deserialization vulnerability, explaining how the autoType bypass can be exploited to achieve arbitrary file reads, SSRF, and other attacks by leveraging gadget classes such as AutoCloseable, and walks through the debugging process and code paths involved.

Programmer DD
Programmer DD
Programmer DD
How Fastjson’s AutoType Bypass Enables File Read and SSRF Attacks

Preface

The analysis of the delayed Fastjson deserialization vulnerability is presented without a public PoC, noting that exploitation methods are diverse. Beyond previously disclosed file‑write techniques, the vulnerability can also be leveraged for SSRF attacks.

1. Vulnerability Overview

Earlier articles showed exploitation by clearing a target file and writing arbitrary content using third‑party libraries. When the gadget class inherits from the first class in the hierarchy, Fastjson’s auto‑type check can be bypassed, allowing the attacker to supply a class that satisfies the expected type check.

This article examines a method that directs deserialization to Fastjson’s built‑in classes, enabling file read, SSRF, and other malicious actions.

2. Debug Analysis

2.1 Vulnerability Debugging

The updated patch adds three methods to expectClass: java.lang.Runnable, java.lang.Readable, and java.lang.AutoCloseable. The parseObject method parses input, extracts the type name, and invokes checkAutoType when the value is not numeric.

if (!allDigits) {
    clazz = config.checkAutoType(typeName, null, lexer.getFeatures());
}

If the supplied data is non‑numeric, expectClass defaults to null and checkAutoType validates the class.

final boolean expectClassFlag;
if (expectClass == null) {
    expectClassFlag = false;
} else {
    if (expectClass == Object.class || expectClass == Serializable.class ||
        expectClass == Cloneable.class || expectClass == Closeable.class ||
        expectClass == EventListener.class || expectClass == Iterable.class ||
        expectClass == Collection.class) {
        expectClassFlag = false;
    } else {
        expectClassFlag = true;
    }
}

The flag remains false because autoCloseable is not on the whitelist or blacklist, and autoTypeSupport is disabled.

The checking process involves three steps:

Hash‑based internal whitelist matching.

Hash‑based internal blacklist matching.

If not in the internal whitelist and autoTypeSupport is enabled (or the expected class is supplied), hash verification against acceptHashCodes and denyHashCodes determines whether the class is loaded or rejected.

clazz = TypeUtils.getClassFromMapping(typeName);

After passing step C, clazz is assigned and further examined.

if (clazz != null) {
    if (expectClass != null && clazz != java.util.HashMap.class &&
        !expectClass.isAssignableFrom(clazz)) {
        throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
    }
    return clazz;
}

The deserializer for the resolved class is obtained and the object is instantiated via JavaBeanDeserializer, which extends MapDeserializer:

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);
return obj;

The deserialization proceeds with the gadget class (e.g., AutoCloseable) and eventually loads the class file:

InputStream is = null;
try {
    String resource = typeName.replace('.', '/') + ".class";
    if (defaultClassLoader != null) {
        is = defaultClassLoader.getResourceAsStream(resource);
    } else {
        is = ParserConfig.class.getClassLoader().getResourceAsStream(resource);
    }
    if (is != null) {
        ClassReader classReader = new ClassReader(is, true);
        TypeCollector visitor = new TypeCollector("<clinit>", new Class[0]);
        classReader.accept(visitor);
        jsonType = visitor.hasJsonType();
    }
} catch (Exception e) {
    // skip
} finally {
    IOUtils.close(is);
}

This step reads the gadget’s bytecode, enabling further exploitation such as arbitrary file reads.

if (autoTypeSupport || jsonType || expectClassFlag) {
    boolean cacheClass = autoTypeSupport || jsonType;
    clazz = TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
if (expectClass != null) {
    if (expectClass.isAssignableFrom(clazz)) {
        TypeUtils.addMapping(typeName, clazz);
        return clazz;
    } else {
        throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
    }
}

The isAssignableFrom check confirms that the loaded class inherits from AutoCloseable, which is the key to the exploit chain.

After the class is added to the internal mapping, Fastjson proceeds with deserialization of the gadget, completing the attack.

2.2 Summary

The critical insight of this vulnerability lies in identifying a commonly used JAR gadget that can be deserialized to trigger malicious behavior, including RCE, arbitrary file read/write, and SSRF. Properly controlling the auto‑type mechanism and whitelist/blacklist configurations is essential to mitigate such attacks.

References

https://b1ue.cn/archives/348.html

https://daybr4ak.github.io/2020/07/20/fastjson%201.6.68%20autotype%20bypass/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDeserializationSSRFAutoType
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.