Author: FearsOff (research team; no individual author bylined)
Published: July 21, 2026 (last updated July 26, 2026)
Source: https://fearsoff.org/research/fastjson-1-2-83-rce
Summary
FearsOff disclosed CVE-2026-16723, a remote code execution vulnerability affecting fastjson 1.2.68 through 1.2.83 — including 1.2.83, the final 1.x release long considered the “safe” version because it ships with AutoType disabled by default. The researchers found that fastjson’s checkAutoType routine reads the bytes of the attacker-supplied @type value with getResourceAsStream before the AutoType blocklist rejects it. Because a resource name can be a URL, this yields a blind server-side request forgery (SSRF) with AutoType off and no gadget class on the classpath. By pointing that fetch at a remote JAR containing a class annotated with @JSONType, and then re-reading the JVM’s still-open cached copy of that JAR through /proc/self/fd, they escalated the SSRF to full RCE that works from JDK 8 through JDK 25 against Spring Boot fat-jar deployments.
Technical Details
Root cause — SSRF inside checkAutoType: When fastjson processes a JSON document containing the special @type key, it treats the value as a Java class name. In 1.2.83, most of checkAutoType is hash-based blocklist checking, but near the end fastjson calls getResourceAsStream on the attacker-controlled typeName to read the class bytes — and it does this before reaching the “autoType is not support” rejection. A resource name can be a URL, so attacker input flows into an outbound fetch even though AutoType (and SafeMode) are off.
Bypassing the dot-to-slash mangling: The code first runs typeName.replace('.', '/'), which shreds a normal hostname (fearsoff.org becomes fearsoff/org) so a plain domain-based URL fetches nothing. The researchers used the classic integer-IP trick — 127.0.0.1 encoded as the single integer 2130706433, which contains no dots — to produce a working URL such as {"@type":"http://2130706433:31337/probe"}. This produced a confirmed blind SSRF straight out of the deserializer.
SSRF to RCE via @JSONType: If the class fetched from the attacker’s JAR is annotated with @JSONType, fastjson loads and instantiates it, again before the AutoType rejection. Instantiating the class runs its static initializer (<clinit>), which is where the attacker’s code lives. The initial approach used a jar:http://host/jar!/Class URL to define the class straight from the remote JAR.
The JDK 9+ class-name restriction: That direct jar:http:// load only works on JDK 8. Starting with JDK 9, defineClass rejects class names containing the double slash in ://, throwing ClassFormatError: Illegal class name. On JDK 9 through 25 the SSRF still fires (the fetch happens first) but the class load dies. The researchers confirmed the target ran JDK 21 (via the Java/21.0.10 User-Agent on the fetch), so the one-shot trick failed there.
The /proc/self/fd trick (the cross-version payload): When the JVM downloads a remote JAR over jar:http, it writes the whole file to a temp file (/tmp/jar_cache<random>.tmp), opens it, then deletes it from disk while keeping the descriptor open — so it remains readable at /proc/self/fd/N. Building on Otto Ebeling’s 2017 Apache Shiro deserialization writeup, the researchers pointed a second @type back at that open descriptor. Because fastjson runs replace('.', '/'), they encoded the path with dots — jar:file:.proc.self.fd.11!.E11 becomes jar:file:/proc/self/fd/11!/E11.class. Crucially, file:/proc/self/fd/... contains only single slashes, so it evades the JDK 9+ :// restriction and the class defines and runs. The descriptor number is not fixed, so exploitation sweeps candidate numbers (e.g. E10, E11, up through a few hundred), stamping out one forged class per candidate and sending one request per number until one lands.
Classloader dependency: The chain hinges on which classloader defines the class. Only Spring Boot’s fat-jar loader, LaunchedURLClassLoader, turns a URL-shaped class name into a real network fetch. The same application run as a plain java -jar, or deployed as a WAR on standard Tomcat, treats the name as a local file path and reaches out to nothing. The researchers note the “one request → RCE” instant win partly reflected a permissive test rig (a custom URLClassLoader set via ParserConfig.getGlobalInstance().setDefaultClassLoader(...)), but the /proc/self/fd variant is a genuine cross-board technique against default-Tomcat Spring Boot fat jars on modern JDKs.
Impact
An unauthenticated attacker who can get an application to deserialize attacker-controlled JSON with fastjson can achieve remote code execution with no gadget class required on the target’s classpath and with AutoType disabled — the configuration previously believed safe. In FearsOff’s engagement the loaded class ran id as root. Affected versions per Alibaba’s advisory are fastjson 1.2.68 through 1.2.83. Practical full RCE (via the /proc/self/fd path) targets applications deployed as Spring Boot fat jars using LaunchedURLClassLoader, and works across JDK 8 through JDK 25. On non–Spring-Boot deployments the primitive degrades to a blind SSRF originating inside the deserializer. Even where RCE conditions aren’t met, the SSRF alone is a meaningful exposure.
Mitigation
Alibaba has published a critical security advisory covering fastjson 1.2.68–1.2.83. Recommended actions:
- Preferred: Migrate to fastjson2 (the 2.x line).
- Stopgap: Enable SafeMode by setting the system property
-Dfastjson.parser.safeMode=true, which disables AutoType handling entirely.
The researchers stress that upgrading to 1.2.83 is no longer sufficient, since the vulnerability is present in 1.2.83 itself. Reducing the attack surface (avoiding deserialization of untrusted JSON, restricting outbound network access from application servers) provides defense in depth against the SSRF primitive.
References
- FastJson 1.2.83 Remote Code Execution
- Alibaba fastjson2 Security Advisory — Remote Code Execution in fastjson 1.2.68–1.2.83
- su18 — fastjson deserialization deep-dive
- su18 — fastjson 1.2.68 commons-io gadget chain
- Otto Ebeling (2017) — JVM deserialization and the /proc/self/fd classloader trick (Apache Shiro)