Fastjson CVE-2026-16723: a Gadget-Free RCE With No Patch to Apply
CVE-2026-16723 is a gadget-free RCE in fastjson 1.2.68–1.2.83, exploited in the wild. Turning AutoType off does not help, and Alibaba has shipped no fixed 1.x release.
For six years the standard answer to "is our fastjson safe?" has been a single line in a runbook: turn AutoType off. Every prior fastjson deserialization bug — 1.2.24, 1.2.47, CVE-2022-25845 — was a bypass of the deny-list that AutoType consults, and every fix tightened that list. CVE-2026-16723, disclosed on 21 July 2026, does not touch the deny-list at all. It abuses the code that runs the check, which means it works with AutoType disabled, with no gadget class anywhere on your classpath, and against a target class you explicitly bound to. Two vendors have confirmed exploitation in the wild. There is no fixed 1.x release to install, and there is not going to be one.
That combination — critical, exploited, unpatchable — is rare enough that it changes what a response even looks like. You cannot close this by scheduling a patch window.
What CVE-2026-16723 Actually Is
The maintainers' advisory, published 21 July 2026 and credited to Kirill Firsov of FearsOff Cybersecurity, is unusually blunt about the preconditions. Unauthenticated remote code execution, in the process's own privilege context, against a stock default configuration.
| Property | Value |
|---|---|
| Affected | fastjson 1.2.68 → 1.2.83 (1.2.83 is the final 1.x release) |
| Not affected | fastjson 2.x, all versions |
| CVSS | 9.0 CRITICAL, AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H (Alibaba as CNA) |
| CWE | CWE-20 (improper input validation) + CWE-502 (deserialization of untrusted data) |
| AutoType | Can be off. Irrelevant to the chain. |
| SafeMode | Must be off — which is the default |
| Packaging | Spring Boot executable fat-JAR, run as java -jar app.jar |
| Confirmed on | Spring Boot 2.x / 3.x / 4.x, JDK 8 / 11 / 17 / 21 |
| Fixed 1.x release | None |
Note the scoring divergence: Alibaba scored it 9.0 with high attack complexity, while NSFOCUS CERT's notice of 22 July rates the same flaw 9.8. NVD has published the record but flagged it not prioritized for enrichment, so there is no NIST score to arbitrate. If your prioritisation pipeline waits for an NVD-enriched score before it acts, this bug will sit in your backlog while it is being exploited.
The three entry points are the ones every Spring Boot service uses: JSON.parse, JSON.parseObject(String), and JSON.parseObject(String, Class). The advisory is explicit that the third one — binding to a concrete DTO, the thing most developers believe is the safe pattern — is not a mitigation, because the payload nests inside any Object- or Map-typed field of the class you bound to.
Why "AutoType Is Off" Stopped Being an Answer
The mechanism is worth understanding precisely, because the intuition it breaks is load-bearing for a lot of Java shops.
When fastjson meets an @type key, it calls ParserConfig.checkAutoType to decide whether the named class is permitted. Before that decision is reached, the function probes for the class as a resource, transforming the supplied name with typeName.replace('.','/') into a path. That probe is a fetch. If the attacker supplies a name shaped like a jar:http://… URL, the JVM performs an outbound request — a server-side request forgery living inside the security check itself.
That is the whole trick. The security control is reached after the dangerous behaviour it was meant to gate. Disabling AutoType configures the decision that never gets to matter.
Two routes then reach code execution, depending on the runtime:
- JDK 8 (direct class route) — the remote
jar:httpprobe loads the class, and the JVM runs its static initializer (<clinit>). The attacker's code executes during the probe, before fastjson attempts any cast. - JDK 17+ (retained file-descriptor route) — the remote fetch caches the JAR behind an open file descriptor. A second probe in the same request body, pointed at
jar:file:/proc/self/fd/N, reopens that descriptor under a legitimate-looking class name, and the annotated class initialises.
The final piece is @JSONType. Fastjson treats that annotation as a trust signal during type resolution: a class carrying it returns early and skips the dangerous-base-class checks. The attacker ships the annotation on their own class, and the library takes its word for it.
"But we bind to a DTO — we get a
ClassCastException, so the payload failed."The
ClassCastExceptionis the receipt, not the block. It is thrown after the attacker's<clinit>has already run during the@typeprobe. If you have those exceptions in your logs alongside unusual@typevalues, that is not evidence of a failed attack.
Everything above is corroborated by a public Docker lab and defensive scanner released after disclosure, which independently documents the checkAutoType probe, the /proc/self/fd reuse, and the @JSONType early return. That repository also claims a wider affected range than the advisory — 1.2.66 through 1.2.83, with probe-bearing code back to 1.2.48. Treat the vendor range as authoritative for remediation and the wider claim as a reason not to feel safe at 1.2.67.
The Spring Boot Fat-JAR Is Both Precondition and Hiding Place
The chain needs Spring Boot's LaunchedURLClassLoader — the loader that exists specifically to resolve nested jar: URLs inside BOOT-INF/lib/ of an executable fat-JAR — to make the attacker's nested JAR path resolvable and reach defineClass. Deployments that are not affected are informative:
- Plain JARs
- Generic uber-JARs (shaded flat, no nested-JAR loader)
- WAR files on Tomcat or Jetty
There is a nasty symmetry here. The packaging format that makes the bug exploitable is the same one that makes the dependency hard to see. A fastjson 1.2.83 pulled in transitively by a reporting SDK or a legacy internal client sits inside BOOT-INF/lib/ of a compiled artifact — invisible to a grep of your source tree, invisible to anyone reading pom.xml for direct dependencies only. Teams that answer "do we use fastjson?" from memory will get it wrong, and the answer they need is per-artifact, not per-repository.
Exploited in the Wild, Absent From KEV
Imperva's threat research reports active exploitation across financial services, healthcare, computing, retail and business targets, almost entirely US-based with some activity in Singapore and Canada. Roughly 30% of the observed attacks come from tools written in Ruby and Go, the rest from browser impersonators — a mix that reads as commodity scanning layered on top of targeted work. ThreatBook added detection on 22 July and confirmed in-the-wild activity the same day.
Now the part that should shape your prioritisation. We pulled the CISA KEV catalog JSON directly this morning — catalog version 2026.07.24, 1,653 entries — and CVE-2026-16723 is not in it. CISA's own exploitation field read "none" as of 23 July. Meanwhile two commercial vendors have production telemetry showing the opposite.
This matters more since CISA replaced the flat 14-day KEV clock of BOD 22-01 with the risk-based tiers of BOD 26-04 in June 2026. Organisations that built their remediation SLA to trigger on KEV membership have, for this CVE, no trigger at all. A vulnerability can be critical, publicly weaponised, and actively exploited while sitting outside the catalog that drives your calendar — and note that a KEV listing would not have helped much anyway, because its remediation instruction would have to be something other than "apply the update."
Remediation
There is no patch. That inverts the usual runbook: exposure discovery and control verification carry the weight that patching normally does. Verify every version and artifact coordinate against the vendor advisory before acting.
1. Am I affected? Three conditions must all hold: a fastjson version in range, fat-JAR packaging, and a network-reachable parse of attacker-controlled JSON.
Resolve the dependency including transitive and shaded copies — a source-tree search will miss the ones that matter:
mvn dependency:tree -Dincludes=com.alibaba:fastjson
./gradlew dependencies --configuration runtimeClasspath | grep -i fastjson
Inspect the built artifacts, which is where the truth lives:
# every fastjson jar shaded inside your deployed fat-JARs
for j in $(find /opt /srv /app -name '*.jar' 2>/dev/null); do
unzip -l "$j" 2>/dev/null | grep -Eo 'fastjson-1\.2\.[0-9]+\.jar' | sed "s|^|$j -> |"
done
# is it a Spring Boot fat-JAR? (the precondition)
unzip -p app.jar META-INF/MANIFEST.MF | grep -E 'Main-Class|Spring-Boot-Version'
And on running hosts:
lsof -p $(pgrep -d, java) 2>/dev/null | grep -i fastjson
Check whether SafeMode is actually set on the running JVM — not whether someone documented it:
ps -ef | grep -o '\-Dfastjson.parser.safeMode=[a-z]*'
jcmd <pid> VM.system_properties | grep fastjson
If that flag is absent, the disabled default applies and the path is open.
2. Patch — exact fixed versions. There are none for 1.x. 1.2.83 is the last release of the line and it is vulnerable. The only true fix is migrating to fastjson2, which the maintainers state is architecturally unaffected in all versions with a secure default configuration.
3. Can't migrate now? Compensating controls. The advisory lists these as priority 0:
- Enable SafeMode by any of the three supported routes:
- JVM flag:
-Dfastjson.parser.safeMode=true - Code:
ParserConfig.getGlobalInstance().setSafeMode(true) - A
fastjson.propertiesentry
- JVM flag:
- Or swap the artifact for
com.alibaba:fastjson:1.2.83_noneautotype
SafeMode disables AutoType wholly and will break any code legitimately relying on polymorphic @type deserialization — find that out in staging, not at 03:00. Add a WAF rule rejecting request bodies that carry @type, as NSFOCUS recommends, and treat it as a speed bump rather than a control: it buys hours for the migration, and it will not survive an encoding trick.
4. Hunt for compromise. Assume the window opened on 21 July at the latest, and search back further. Signals, mapped to MITRE ATT&CK:
@typevalues in request bodies carryingjar:,http://orhttps://seeds — T1190 (exploit public-facing application). Search your gateway and access logs, including bodies if you retain them./proc/self/fd/or/dev/fd/strings inside JSON request bodies, in a single request or correlated across requests from one source. There is no legitimate reason for these to appear in an API payload — it is the single highest-fidelity indicator here.- Outbound HTTP from a JVM that should only receive it. The probe is a fetch: an application server reaching out to an unfamiliar host to retrieve a JAR is T1105 (ingress tool transfer), and it is the point at which the attack becomes visible on the wire even if the payload was encoded.
- Child processes of the
javaprocess — shells,curl,wget, package managers — T1059 (command and scripting interpreter). A Spring Boot service forking a shell is close to always wrong. ClassCastExceptionclusters co-occurring with unusual@typevalues, per the note above.
The scanner published with the public lab automates the first two passes: fjdetect.py triages logs for remote-JAR seeds and file-descriptor sequences, fjscan_static.py inventories JAR/WAR/EAR artifacts for the vulnerable fastjson + Spring Boot loader combination and exits non-zero on exposure, and fjscan_probe.py performs a safe reachability check against a collaborator canary without executing a payload.
5. Eradicate and verify. If you find execution, patching the config does not undo it:
- Rebuild the affected hosts or containers from a known-good image. Code ran as the Java process; treat in-memory implants and any dropped artifact as possible.
- Rotate everything the JVM could read: datasource credentials, keystore contents, JWT signing keys, cloud instance-role and service-account tokens, and any secret injected as an environment variable — the process had them all.
- Review outbound connections from the affected hosts for the whole window, not just around the exploit timestamp.
- Verify last, after the control is in place: re-run the reachability probe against your own endpoint and confirm the fetch no longer occurs. A configuration flag that is set and a code path that is closed are two different claims, and only the second one is worth reporting.
Where Zero Hunt Fits
Everything above converges on one question, and it is not a question a scanner answers well. With no patch to apply, "are we safe?" reduces to two empirical claims: is this parser actually reachable with attacker-controlled JSON in my deployment, and does the compensating control I just enabled actually close the path here. Both are statements about exploitability in a specific environment, and both are answered by attacking it.
That is the case Zero Hunt's AI generative pentest engine is built for. The 10-agent swarm — Recon, Exploit, Web, Credential, Post-Exploit, Pivot, Tactic, Report, under an AI Controller — writes exploit code per target with a local LLM rather than replaying a public PoC. The distinction is the whole point here: the published fastjson chain assumes JDK 8 or a /proc/self/fd route on Linux, a particular loader, a particular reachable endpoint. Your deployment differs in some of those, and a replayed PoC that fails tells you nothing about whether a differing chain succeeds. Generating the chain against your artifact is what separates "the canned exploit didn't work" from "this is not exploitable here." Every attempt runs in an ephemeral Docker container with optional gVisor hardening, so validating an RCE against a production-shaped target does not put the appliance at risk, and every skill is backtested in AI Gym against Vulhub-Bench and NYU CTF Bench before it touches a live environment.
One honest note from our own corpus, offered as an internal observation rather than an industry statistic: CVE-2026-16723 arrived in our threat-intelligence sync from NVD within a day, and our AI Gym Knowledge RAG matched it at 0.81 similarity against fastjson's bypass lineage — CVE-2022-25845, CVE-2025-70974, and our own Nuclei templates for the older 1.2.62 and 1.2.68 gadget chains. But the multi-hop analysis returned low confidence on the chain itself and recommended exploratory testing with custom payloads. That is the correct answer, and it is the argument in miniature: this bug does not resemble the fastjson CVEs that came before it, so a system that prioritises by pattern-matching prior vulnerabilities will under-rate it. Something has to actually try the exploit.
The compliance side follows from the same fact. Unpatchable components are exactly where NIS2 and DORA risk-treatment documentation gets tested, because the auditor's question is not "did you patch" — you cannot — but "what did you do instead, and how do you know it worked." Zero Hunt maps each finding and each re-test against the 32 supported frameworks and signs it with ECDSA at write time, so the evidence that SafeMode was verified effective on a named date, against a named artifact, is a chain-of-custody record in the Trust Center bundle rather than a screenshot in a ticket.
If you run Java services, the useful next hour is not spent reading more analysis. It is spent running mvn dependency:tree across your build estate and unzip -l across your deployed artifacts. Most teams will find at least one 1.2.8x in there that nobody remembered adding.
Want to see the difference between a scanner reporting a version string and an engine proving reachability? Talk to us or read how the platform is put together.