
Un ricercatore di sicurezza offensivo + una IA vs. un n-day fresco: costruire il primo PoC pubblico per CVE-2026-53435 in una notte di venerdì. Log grezzo di 8h20m all'interno.
Prima prova di concetto pubblica per CVE-2026-53435, creata quando esisteva solo l'avviso e nessun PoC era stato pubblicato da nessuna parte. Ricostruita da un vago avviso di una riga in circa ~8.5 ore un venerdì sera — non scrivendo l'exploit a mano, ma dirigendo un'IA attraverso vicoli ciechi, cambi di rotta e verifiche fino a ottenere una catena funzionante.

Il PoC che raggiunge /etc/passwd sul controller — lettura arbitraria di file confermata.
| CVE | CVE-2026-53435 (Jenkins SECURITY-3707) |
| Classe | Deserializzazione non sicura (bypass del ClassFilter tramite config.xml) |
| Impatto (questo PoC) | Lettura arbitraria di file autenticata sul controller |
| Versioni affette | Jenkins settimanale ≤ 2.567, LTS ≤ 2.555.2 |
| Corretto in | Jenkins settimanale 2.568, LTS 2.555.3 |
| Avviso | https://www.jenkins.io/security/advisory/2026-06-10/ (pubblicato il 2026-06-10) |
| Stato al momento della scrittura | Nessun PoC pubblico esisteva (repo PoC su GitHub: 0) |
Questo è un esperimento per mostrare il processo, non solo il manufatto finale.
Per anni, il risultato di un ricercatore offensivo era un singolo exploit finito — le 8 ore di percorsi sbagliati che lo hanno prodotto sparivano. Questo repository conserva i percorsi sbagliati. La trascrizione completa e leggermente modificata di un umano + IA che lavora su un n-day fresco, dall'avviso al PoC funzionante, è inclusa come appendice.
Il punto che vuole dimostrare è deliberatamente non "l'IA l'ha fatto." Le 8 ore sono la prova che non è stato così. L'IA non ha creato la catena in un colpo solo. Ciò che ha portato avanti il lavoro è stata l'esperienza di dominio — sapere che l'avviso nascondeva un sink di deserializzazione, sapere dove cercare nei tipi core di Jenkins piuttosto che nei plugin, sapere che un DescribableList pre-patch non imponeva il tipo degli elementi, e sapere come verificare un'affermazione invece di fidarsi. Il valore del ricercatore non è scomparso; si è spostato dal digitare l'exploit al guidare, potare e validare.
Questo cambiamento è la cosa che vale la pena documentare.
Jenkins protegge la deserializzazione con un ClassFilter personalizzato che permette solo tipi definiti nel core di Jenkins o nei plugin. CVE-2026-53435 consiste nel fatto che questo non è sufficiente: un attaccante che può inviare un config.xml può indurre Jenkins a deserializzare un tipo arbitrario core/plugin in un contesto che non se lo aspettava, e poi raggiungere quell'oggetto via HTTP tramite il routing di Stapler.
Questo PoC inserisce un hudson.Plugin$DummyImpl (un tipo del core di Jenkins, quindi supera la whitelist di localizzazione) nella lista <properties> di un ListView — un DescribableList<ViewProperty> che, pre-patch, non impone il tipo degli elementi. L'oggetto inserito trasporta baseResourceURL=file:/. Instradare una richiesta HTTP a esso serve quindi i file direttamente dal filesystem del controller.
Una catena ysoserial generica non funziona qui — il gadget deve essere un tipo residente in Jenkins/plugin per superare il filtro. Questo vincolo è il vero gioco.
Usa solo contro sistemi che possiedi o per i quali hai esplicitamente l'autorizzazione al test.
L'exploit è stato eseguito contro due contenitori con configurazione identica — 2.555.2 vulnerabile e 2.555.3 corretto — per confermare che esercita questa CVE e non un qualche primitivo di lettura file non correlato:
| 2.555.2 (vulnerabile) | 2.555.3 (corretto) | |
|---|---|---|
createView | HTTP 200 | HTTP 200 |
<properties> memorizzato | <hudson.Plugin_-DummyImpl/> — sopravvive | <properties/> — rimosso |
Trigger → /etc/passwd | root:x:0:0:... — letto | vuoto — bloccato |
La build corretta accetta la richiesta ma la correzione rifiuta il tipo inserito al momento della deserializzazione, quindi la lista delle proprietà torna vuota e non c'è nulla a cui instradare. Il fallimento cade esattamente sul meccanismo descritto nell'avviso — il che rende questo un vero PoC per CVE-2026-53435.
python3 exploit_cve_2026_53435_v2.py <base_url> <user> <pass> <remote_file> [view_name]
# esempio (contro il tuo laboratorio):
python3 exploit_cve_2026_53435_v2.py http://127.0.0.1:8080 <user> <pass> /etc/passwd
base_url — http://host:port del target (HTTP semplice; non anteporre https a meno che l'istanza non gestisca effettivamente TLS).config.xml di vista (View/Configure), oltre a Overall/Read.docker compose -f lab/docker-compose.yml up -d
# vulnerabile: http://127.0.0.1:8080 (Jenkins LTS 2.555.2)
# corretto: http://127.0.0.1:8081 (Jenkins LTS 2.555.3, controllo negativo)
Il laboratorio predispone un database utenti locale tramite inizializzazione Groovy in modo che il percorso autenticato a bassi privilegi possa essere esercitato senza un IdP esterno.
2026-06-12 (Ven) 15:00 → 23:23 ≈ 8h 20m
15:00–15:12 Laboratorio costruito (docker: vuln 2.555.2 / corretto 2.555.3) + prima ricognizione
15:12–21:00 Analisi del sink di deserializzazione e del gadget
(tentativi di bypass di Commons-Collections falliti → pivot
dalle proprietà alle azioni e viceversa)
21:00–22:30 Analisi del ClassFilter + nuova ricerca di gadget, limitata ai tipi core
22:30–23:00 PoC funzionante (v1 → v2) + verifica differenziale (vuln vs corretto)
La maggior parte di quelle ore è stata analisi, non digitazione. L'exploit stesso è stato assemblato negli ultimi ~30 minuti — una volta noti il sink e il gadget giusti.
Costruito con Claude Code (Anthropic), guidato interattivamente dall'autore:
La trascrizione completa e leggermente modificata del lavoro è in
transcript/ — tenuta grezza, compresi i vicoli ciechi. (Modificata solo per addolcire alcune righe di tono e per mascherare dettagli di rete locali; nulla di tecnico è stato rimosso.)
Solo per test di sicurezza autorizzati, ricerca e istruzione. L'autore e i collaboratori non accettano alcuna responsabilità per un uso improprio. La vulnerabilità presa di mira è stata corretta — aggiorna a Jenkins LTS 2.555.3 / settimanale 2.568 o successivo.