Blog
FastjsonCVE-2026-16723Deserializzazione JavaSpring Boot

Fastjson CVE-2026-16723: RCE senza gadget e nessuna patch disponibile

CVE-2026-16723 è una RCE senza gadget in fastjson 1.2.68–1.2.83, già sfruttata in rete. Disattivare AutoType non serve, e per il ramo 1.x non esiste alcuna release corretta.

Zero Hunt Research··12 min di lettura

Da sei anni la risposta standard alla domanda "il nostro fastjson è sicuro?" sta in una riga di runbook: disattiva AutoType. Ogni precedente bug di deserializzazione di fastjson — 1.2.24, 1.2.47, CVE-2022-25845 — era un bypass della deny-list consultata da AutoType, e ogni fix stringeva quella lista. CVE-2026-16723, divulgata il 21 luglio 2026, la deny-list non la tocca affatto: abusa del codice che esegue il controllo. Funziona quindi con AutoType disattivato, senza alcuna gadget class nel classpath e anche contro una classe di destinazione dichiarata esplicitamente. Due vendor hanno confermato lo sfruttamento in rete. Non esiste una release 1.x corretta da installare, e non arriverà.

Questa combinazione — critica, sfruttata, non correggibile — è abbastanza rara da cambiare la forma stessa della risposta. Non si chiude pianificando una finestra di patching.

Che cosa è davvero CVE-2026-16723

L'advisory dei manutentori, pubblicato il 21 luglio 2026 e attribuito a Kirill Firsov di FearsOff Cybersecurity, è insolitamente esplicito sulle precondizioni. Esecuzione di codice remoto non autenticata, con i privilegi del processo Java, su configurazione di default.

Proprietà Valore
Versioni affette fastjson 1.2.68 → 1.2.83 (1.2.83 è l'ultima release del ramo 1.x)
Non affette fastjson 2.x, tutte le versioni
CVSS 9.0 CRITICAL, AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H (Alibaba come CNA)
CWE CWE-20 (validazione input impropria) + CWE-502 (deserializzazione di dati non fidati)
AutoType Può essere disattivato. Irrilevante per la catena.
SafeMode Deve essere disattivato — cioè il default
Packaging fat-JAR eseguibile Spring Boot, avviato con java -jar app.jar
Verificato su Spring Boot 2.x / 3.x / 4.x, JDK 8 / 11 / 17 / 21
Release 1.x corretta Nessuna

Da notare la divergenza nel punteggio: Alibaba assegna 9.0 con complessità di attacco alta, mentre il bollettino NSFOCUS CERT del 22 luglio valuta la stessa falla 9.8. NVD ha pubblicato il record ma lo ha marcato not prioritized for enrichment, quindi non esiste un punteggio NIST a fare da arbitro. Se la vostra pipeline di prioritizzazione aspetta un punteggio NVD arricchito prima di agire, questo bug resterà in backlog mentre viene sfruttato.

I tre punti di ingresso sono quelli che usa qualunque servizio Spring Boot: JSON.parse, JSON.parseObject(String) e JSON.parseObject(String, Class). L'advisory è esplicito sul terzo — il binding a un DTO concreto, cioè il pattern che la maggior parte degli sviluppatori considera sicuro: non è una mitigazione, perché il payload si annida in qualsiasi campo di tipo Object o Map della classe a cui avete fatto il binding.

Perché "AutoType è disattivato" ha smesso di essere una risposta

Il meccanismo merita precisione, perché l'intuizione che demolisce regge il modello mentale di parecchie architetture Java.

Quando fastjson incontra una chiave @type, invoca ParserConfig.checkAutoType per decidere se la classe indicata sia ammessa. Prima che quella decisione venga presa, la funzione sonda la classe come risorsa, trasformando il nome fornito con typeName.replace('.','/') in un percorso. Quella sonda è un fetch. Se l'attaccante fornisce un nome nella forma jar:http://…, la JVM effettua una richiesta in uscita: una server-side request forgery che vive dentro il controllo di sicurezza stesso.

Il trucco è tutto qui. Il controllo viene raggiunto dopo il comportamento pericoloso che avrebbe dovuto regolare. Disattivare AutoType configura una decisione che non arriva mai a contare.

Da lì due percorsi arrivano all'esecuzione di codice, a seconda del runtime:

  • JDK 8 (percorso a classe diretta) — la sonda remota jar:http carica la classe e la JVM ne esegue l'inizializzatore statico (<clinit>). Il codice dell'attaccante viene eseguito durante la sonda, prima che fastjson tenti qualsiasi cast.
  • JDK 17+ (percorso a file descriptor trattenuto) — il fetch remoto lascia il JAR in cache dietro un file descriptor aperto. Una seconda sonda, nello stesso corpo della richiesta, puntata a jar:file:/proc/self/fd/N, riapre quel descriptor sotto un nome di classe legittimo e la classe annotata viene inizializzata.

L'ultimo tassello è @JSONType. Durante la type resolution fastjson tratta quell'annotazione come un segnale di fiducia: una classe che la espone provoca un'uscita anticipata che salta i controlli sulle classi base pericolose. L'attaccante mette l'annotazione sulla propria classe, e la libreria gli crede sulla parola.

"Ma noi facciamo binding su un DTO — otteniamo una ClassCastException, quindi il payload è fallito."

La ClassCastException è la ricevuta, non il blocco. Viene sollevata dopo che il <clinit> dell'attaccante è già stato eseguito durante la sonda su @type. Se avete quelle eccezioni nei log insieme a valori @type anomali, non è la prova di un attacco fallito.

Tutto quanto sopra è corroborato da un lab Docker pubblico con scanner difensivo rilasciato dopo la divulgazione, che documenta in modo indipendente la sonda in checkAutoType, il riuso di /proc/self/fd e l'uscita anticipata su @JSONType. Quel repository dichiara anche un intervallo più ampio rispetto all'advisory — dalla 1.2.66 alla 1.2.83, con codice che effettua la sonda già dalla 1.2.48. Considerate autoritativo l'intervallo del vendor per la remediation, e la rivendicazione più ampia come un buon motivo per non sentirvi tranquilli sulla 1.2.67.

Il fat-JAR Spring Boot è insieme precondizione e nascondiglio

La catena ha bisogno del LaunchedURLClassLoader di Spring Boot — il classloader che esiste proprio per risolvere URL jar: annidati dentro BOOT-INF/lib/ di un fat-JAR eseguibile — per rendere risolvibile il percorso del JAR dell'attaccante e arrivare a defineClass. I deployment che non sono affetti sono istruttivi:

  • JAR semplici
  • uber-JAR generici (shaded piatti, senza classloader per JAR annidati)
  • file WAR su Tomcat o Jetty

C'è una simmetria sgradevole. Il formato di packaging che rende il bug sfruttabile è lo stesso che rende la dipendenza difficile da vedere. Un fastjson 1.2.83 tirato dentro in modo transitivo da un SDK di reportistica o da un vecchio client interno vive dentro BOOT-INF/lib/ di un artefatto compilato: invisibile a un grep sul sorgente, invisibile a chi legge il pom.xml guardando solo le dipendenze dirette. I team che rispondono "usiamo fastjson?" a memoria sbaglieranno, e la risposta che serve è per artefatto, non per repository.

Sfruttata in rete, assente dal KEV

La ricerca di Imperva riporta sfruttamento attivo verso servizi finanziari, sanità, informatica, retail e imprese, quasi interamente su bersagli statunitensi con qualche attività a Singapore e in Canada. Circa il 30% degli attacchi osservati proviene da tool scritti in Ruby e Go, il resto da browser impersonator: un mix che si legge come scansione di massa sovrapposta a lavoro mirato. ThreatBook ha aggiunto la detection il 22 luglio e ha confermato attività in rete lo stesso giorno.

Ora il punto che dovrebbe orientare la vostra prioritizzazione. Abbiamo scaricato direttamente il JSON del catalogo CISA KEV questa mattina — versione di catalogo 2026.07.24, 1.653 voci — e CVE-2026-16723 non c'è. Il campo sullo sfruttamento di CISA riportava "none" al 23 luglio. Nel frattempo due vendor commerciali hanno telemetria di produzione che dice il contrario.

La cosa pesa ancora di più da quando, a giugno 2026, CISA ha sostituito il termine fisso di 14 giorni della BOD 22-01 con i livelli basati sul rischio della BOD 26-04. Le organizzazioni che hanno costruito la propria SLA di remediation sull'ingresso nel KEV, per questa CVE, non hanno alcun innesco. Una vulnerabilità può essere critica, pubblicamente armata e attivamente sfruttata pur restando fuori dal catalogo che detta il vostro calendario — e va detto che l'inserimento nel KEV sarebbe servito a poco, perché la sua istruzione di remediation dovrebbe essere qualcosa di diverso da "applicate l'aggiornamento".

Remediation

Non c'è patch. Questo ribalta il runbook abituale: la scoperta dell'esposizione e la verifica del controllo si prendono il peso che di norma è del patching. Verificate ogni versione e ogni coordinata dell'artefatto sull'advisory del vendor prima di agire.

1. Sono esposto? Devono valere tutte e tre le condizioni: una versione di fastjson nell'intervallo, packaging fat-JAR e un parsing raggiungibile dalla rete di JSON controllato dall'attaccante.

Risolvete la dipendenza includendo le copie transitive e shaded — una ricerca sul sorgente perde proprio quelle che contano:

mvn dependency:tree -Dincludes=com.alibaba:fastjson
./gradlew dependencies --configuration runtimeClasspath | grep -i fastjson

Ispezionate gli artefatti compilati, dove sta la verità:

# ogni jar di fastjson shaded dentro i vostri fat-JAR in produzione
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

# è un fat-JAR Spring Boot? (la precondizione)
unzip -p app.jar META-INF/MANIFEST.MF | grep -E 'Main-Class|Spring-Boot-Version'

E sugli host in esecuzione:

lsof -p $(pgrep -d, java) 2>/dev/null | grep -i fastjson

Verificate che SafeMode sia davvero impostato sulla JVM in esecuzione — non che qualcuno lo abbia documentato:

ps -ef | grep -o '\-Dfastjson.parser.safeMode=[a-z]*'
jcmd <pid> VM.system_properties | grep fastjson

Se il flag non c'è, vale il default disattivato e il percorso è aperto.

2. Patch — versioni corrette esatte. Per il ramo 1.x non ne esistono. La 1.2.83 è l'ultima release della linea ed è vulnerabile. L'unico fix reale è la migrazione a fastjson2, che i manutentori dichiarano architetturalmente non affetto in tutte le versioni, con configurazione di default sicura.

3. Non potete migrare subito? Controlli compensativi. L'advisory li elenca come priorità 0:

  • Abilitare SafeMode per una delle tre vie supportate:
    • flag JVM: -Dfastjson.parser.safeMode=true
    • da codice: ParserConfig.getGlobalInstance().setSafeMode(true)
    • una voce in fastjson.properties
  • Oppure sostituire l'artefatto con com.alibaba:fastjson:1.2.83_noneautotype

SafeMode disabilita AutoType per intero e romperà qualsiasi codice che dipenda legittimamente dalla deserializzazione polimorfica via @type: scopritelo in staging, non alle 03:00. Aggiungete una regola WAF che rifiuti i corpi di richiesta contenenti @type, come raccomanda NSFOCUS, e trattatela come un dosso e non come un controllo: compra ore per la migrazione, e non sopravvive a un trucco di encoding.

4. Cercate segni di compromissione. Assumete che la finestra si sia aperta al più tardi il 21 luglio, e cercate anche più indietro. I segnali, mappati su MITRE ATT&CK:

  • Valori @type nei corpi delle richieste che contengono jar:, http:// o https:// — T1190 (exploit public-facing application). Cercate nei log di gateway e access, corpi inclusi se li conservate.
  • Stringhe /proc/self/fd/ o /dev/fd/ dentro corpi JSON, in una singola richiesta o correlate tra richieste dalla stessa sorgente. Non esiste una ragione legittima perché compaiano in un payload API: è l'indicatore a più alta fedeltà del caso.
  • Traffico HTTP in uscita da una JVM che dovrebbe solo riceverne. La sonda è un fetch: un application server che contatta un host sconosciuto per scaricare un JAR è T1105 (ingress tool transfer), ed è il punto in cui l'attacco diventa visibile sulla rete anche se il payload era offuscato.
  • Processi figli del processo java — shell, curl, wget, package manager — T1059 (command and scripting interpreter). Un servizio Spring Boot che forka una shell è quasi sempre sbagliato.
  • Grappoli di ClassCastException in concomitanza con valori @type anomali, per quanto detto sopra.

Lo scanner pubblicato insieme al lab pubblico automatizza i primi due passaggi: fjdetect.py fa triage dei log cercando seed di JAR remoti e sequenze di file descriptor, fjscan_static.py inventaria artefatti JAR/WAR/EAR alla ricerca della combinazione fastjson vulnerabile + classloader Spring Boot uscendo con codice diverso da zero in caso di esposizione, e fjscan_probe.py esegue un controllo di raggiungibilità sicuro contro una canary collaborator senza eseguire alcun payload.

5. Bonificate e verificate. Se trovate esecuzione, sistemare la configurazione non la annulla:

  • Ricostruite host e container affetti da un'immagine nota come integra. Il codice ha girato con i privilegi del processo Java: considerate possibili impianti in memoria e qualunque artefatto lasciato a terra.
  • Ruotate tutto ciò che la JVM poteva leggere: credenziali dei datasource, contenuto dei keystore, chiavi di firma JWT, token di instance-role cloud e service account, e ogni segreto iniettato come variabile d'ambiente — il processo li aveva tutti.
  • Rivedete le connessioni in uscita dagli host affetti per l'intera finestra, non solo attorno al timestamp dell'exploit.
  • Verificate per ultimo, dopo aver messo in atto il controllo: rilanciate la sonda di raggiungibilità contro il vostro stesso endpoint e confermate che il fetch non avvenga più. "Il flag è impostato" e "il percorso è chiuso" sono due affermazioni diverse, e solo la seconda vale la pena di essere riportata.

Dove si colloca Zero Hunt

Tutto quanto sopra converge su una domanda sola, e non è una domanda a cui uno scanner risponda bene. Senza patch da applicare, "siamo al sicuro?" si riduce a due affermazioni empiriche: questo parser è davvero raggiungibile con JSON controllato dall'attaccante nel mio deployment, e il controllo compensativo che ho appena abilitato chiude davvero il percorso qui. Entrambe sono affermazioni sulla sfruttabilità in un ambiente specifico, ed entrambe si verificano attaccandolo.

È esattamente il caso per cui è costruito il motore di pentest generativo AI di Zero Hunt. Lo swarm di 10 agenti — Recon, Exploit, Web, Credential, Post-Exploit, Pivot, Tactic, Report, sotto un AI Controller — scrive codice di exploit per bersaglio con un LLM locale invece di rigiocare un PoC pubblico. Qui la distinzione è tutto: la catena fastjson pubblicata presuppone JDK 8 oppure il percorso /proc/self/fd su Linux, un certo classloader, un certo endpoint raggiungibile. Il vostro deployment differisce su qualcuno di questi punti, e un PoC rigiocato che fallisce non dice nulla su una catena diversa che invece riuscirebbe. Generare la catena contro il vostro artefatto è ciò che separa "l'exploit preconfezionato non ha funzionato" da "qui non è sfruttabile". Ogni tentativo gira in un container Docker effimero con hardening gVisor opzionale, quindi validare una RCE contro un bersaglio simile alla produzione non mette a rischio l'appliance, e ogni skill viene sottoposta a backtest in AI Gym su Vulhub-Bench e NYU CTF Bench prima di toccare un ambiente reale.

Una nota onesta dal nostro corpus, da leggere come osservazione interna e non come statistica di settore: CVE-2026-16723 è arrivata nel nostro sync di threat intelligence da NVD nel giro di un giorno, e la nostra Knowledge RAG di AI Gym l'ha correlata con similarità 0,81 alla genealogia dei bypass di fastjson — CVE-2022-25845, CVE-2025-70974 e i nostri template Nuclei per le vecchie catene con gadget 1.2.62 e 1.2.68. Ma l'analisi multi-hop ha restituito confidenza bassa sulla catena in sé, raccomandando test esplorativi con payload custom. È la risposta corretta, ed è l'argomento in miniatura: questo bug non somiglia alle CVE fastjson che l'hanno preceduto, quindi un sistema che prioritizza per somiglianza con vulnerabilità passate lo sottovaluterà. Qualcuno deve provare davvero l'exploit.

Il versante compliance discende dallo stesso fatto. I componenti non correggibili sono esattamente il punto in cui la documentazione di trattamento del rischio richiesta da NIS2 e DORA viene messa alla prova, perché la domanda dell'auditor non è "avete applicato la patch" — non potete — ma "che cosa avete fatto al suo posto, e come sapete che ha funzionato". Zero Hunt mappa ogni finding e ogni ri-test sui 32 framework supportati e li firma con ECDSA al momento della scrittura, così la prova che SafeMode è stato verificato efficace in una data precisa, su un artefatto preciso, è un record di chain-of-custody nel bundle del Trust Center e non uno screenshot dentro un ticket.

Se avete servizi Java, l'ora successiva non va spesa leggendo altre analisi. Va spesa lanciando mvn dependency:tree su tutto il parco build e unzip -l sugli artefatti in produzione. La maggior parte dei team ci troverà almeno una 1.2.8x che nessuno ricordava di aver aggiunto.

Volete vedere la differenza tra uno scanner che riporta una stringa di versione e un motore che dimostra la raggiungibilità? Parlatene con noi o guardate com'è fatta la piattaforma.