
Un chercheur en sécurité offensive + une IA contre un n-day frais : construire le premier PoC public pour CVE-2026-53435 en une nuit de vendredi. Log brut de 8h20m à l'intérieur.
Première preuve de concept publique pour CVE-2026-53435, construite alors que seul l'avis existait et qu'aucun PoC n'était publié nulle part. Reconstruite à partir d'un avis vague d'une ligne en ~8,5 heures un vendredi soir — non pas en écrivant l'exploit à la main, mais en dirigeant une IA à travers les impasses, les pivots et la vérification jusqu'à ce qu'une chaîne fonctionnelle émerge.

Le PoC atteignant
/etc/passwdsur le contrôleur — lecture arbitraire de fichiers confirmée.
| CVE | CVE-2026-53435 (Jenkins SECURITY-3707) |
| Classe | Désérialisation non sécurisée (contournement de ClassFilter via config.xml) |
| Impact (ce PoC) | Lecture arbitraire de fichiers authentifiée sur le contrôleur |
| Affecté | Jenkins weekly ≤ 2.567, LTS ≤ 2.555.2 |
| Corrigé | Jenkins weekly 2.568, LTS 2.555.3 |
| Avis | https://www.jenkins.io/security/advisory/2026-06-10/ (publié le 2026-06-10) |
| Statut au moment de la rédaction | Aucun PoC public n'existait (dépôts GitHub PoC : 0) |
C'est une expérience pour montrer le processus, pas seulement l'artefact.
Pendant des années, le résultat d'un chercheur offensif était un seul exploit finalisé — les 8 heures de mauvais tournants qui l'ont produit disparaissaient. Ce dépôt conserve les mauvais tournants. La transcription complète et légèrement éditée d'un humain + IA travaillant sur un n-day frais, de l'avis au PoC fonctionnel, est incluse en annexe.
Le point qu'il soulève est délibérément pas « l'IA l'a fait. » Les 8 heures sont la preuve que ce n'est pas le cas. L'IA n'a jamais réalisé la chaîne en un seul coup. Ce qui a porté le travail, c'est l'expertise du domaine — savoir que l'avis cachait un point de désérialisation, savoir chercher dans les types core de Jenkins plutôt que dans les plugins, savoir qu'un DescribableList avant le correctif n'appliquait pas son type d'élément, et savoir comment vérifier une affirmation au lieu de lui faire confiance. La valeur du chercheur n'a pas disparu ; elle est passée de saisir l'exploit à diriger, élaguer et valider.
Ce changement est ce qui mérite d'être documenté.
Jenkins protège la désérialisation avec un ClassFilter personnalisé qui n'autorise que les types définis dans le cœur de Jenkins ou les plugins. CVE-2026-53435 signifie que cela ne suffit pas : un attaquant capable de POSTer un config.xml peut amener Jenkins à désérialiser un type arbitraire du cœur/plugin dans un contexte qui ne l'attendait jamais, puis accéder à cet objet via HTTP via le routage Stapler.
Ce PoC plante un hudson.Plugin$DummyImpl (un type du cœur de Jenkins, donc il passe la liste blanche d'emplacement) dans la liste <properties> d'une ListView — une DescribableList<ViewProperty> qui, avant le correctif, n'applique pas son type d'élément. L'objet planté porte baseResourceURL=file:/. Router une requête HTTP vers celui-ci sert alors des fichiers directement depuis le système de fichiers du contrôleur.
Une chaîne ysoserial générique ne fonctionne pas ici — le gadget doit être un type résidant dans Jenkins/plugin pour survivre au filtre. Cette contrainte est tout l'enjeu.
À utiliser uniquement contre des systèmes que vous possédez ou que vous êtes explicitement autorisé à tester.
L'exploit a été exécuté contre deux conteneurs de configuration identique — vulnérable 2.555.2 et corrigé 2.555.3 — pour confirmer qu'il exerce ce CVE et non une primitive de lecture de fichiers sans rapport :
| 2.555.2 (vulnérable) | 2.555.3 (corrigé) | |
|---|---|---|
createView | HTTP 200 | HTTP 200 |
<properties> stocké | <hudson.Plugin_-DummyImpl/> — survit | <properties/> — supprimé |
Déclencheur → /etc/passwd | root:x:0:0:... — lu | vide — bloqué |
La version corrigée accepte la requête mais le correctif rejette le type planté au moment de la désérialisation, donc la liste des propriétés revient vide et il n'y a rien à router. L'échec atterrit exactement sur le mécanisme que l'avis décrit — ce qui fait de ceci un véritable PoC pour CVE-2026-53435.
python3 exploit_cve_2026_53435_v2.py <base_url> <user> <pass> <remote_file> [view_name]
# example (against your own lab):
python3 exploit_cve_2026_53435_v2.py http://127.0.0.1:8080 <user> <pass> /etc/passwd
base_url — http://host:port de la cible (HTTP simple ; ne pas préfixer https sauf si l'instance termine réellement TLS).config.xml de vue (View/Configure), plus Overall/Read.docker compose -f lab/docker-compose.yml up -d
# vulnerable: http://127.0.0.1:8080 (Jenkins LTS 2.555.2)
# patched: http://127.0.0.1:8081 (Jenkins LTS 2.555.3, negative control)
Le laboratoire provisionne une base de données utilisateur locale uniquement via l'initialisation Groovy afin que le chemin à faible privilège authentifié puisse être exercé sans fournisseur d'identité externe.
2026-06-12 (Fri) 15:00 → 23:23 ≈ 8h 20m
15:00–15:12 Lab built (docker: vuln 2.555.2 / patched 2.555.3) + first recon
15:12–21:00 Deserialization sink & gadget analysis
(Commons-Collections bypass attempts failed → pivot
from properties to actions and back)
21:00–22:30 ClassFilter analysis + gadget re-hunt, restricted to core types
22:30–23:00 Working PoC (v1 → v2) + differential verification (vuln vs patched)
La plupart de ces heures étaient de l'analyse, pas de la frappe. L'exploit lui-même s'est assemblé dans les dernières ~30 minutes — une fois le bon point de désérialisation et le bon gadget connus.
Construit avec Claude Code (Anthropic), piloté interactivement par l'auteur :
La transcription complète et légèrement éditée du travail est dans transcript/ — conservée brute, y compris les impasses. (Éditée seulement pour adoucir quelques lignes de ton et masquer les détails du réseau local ; rien de technique n'a été supprimé.)
Pour les tests de sécurité autorisés, la recherche et l'éducation uniquement. L'auteur et les contributeurs déclinent toute responsabilité en cas d'utilisation abusive. La vulnérabilité ciblée est corrigée — mettez à jour vers Jenkins LTS 2.555.3 / weekly 2.568 ou ultérieur.