
Trouvez la vulnérabilité que vos tests n'ont jamais été conçus pour détecter. Une démo ReGrade modélisant CVE-2023-5968 : détectez une fuite de hachage de mot de passe en comparant une application à elle-même.
Une démonstration pratique de la découverte de zero-day avec ReGrade. Vous dirigerez une suite de tests ordinaire vers ReGrade, l'enregistrerez contre un petit service, la rejouerez contre une seconde copie du même service, et utiliserez Claude Code + les outils MCP de ReGrade pour révéler une fuite de hachage de mot de passe — une vulnérabilité pour laquelle aucun test n'a été écrit.
Elle modélise un vrai bug : CVE-2023-5968, où le point de terminaison de mise à jour du nom d'utilisateur d'une plateforme de collaboration renvoyait l'objet utilisateur complet incluant le hachage bcrypt du mot de passe. Il a été livré en 2017 et a survécu 7 ans de tests, revues et audits. (Article de Curtail.)
Le rebondissement : il n'y a pas de v2. Vous comparez l'application à elle-même. Une nouvelle instance hache ses mots de passe avec des sels bcrypt différents, donc les valeurs du hachage fuité diffèrent entre les deux copies — et c'est cette entropie que ReGrade signale. La vulnérabilité est latente dans le code que vous avez déjà livré ; aucun changement de version n'est nécessaire pour la trouver.
regrade depuis https://app.regrade.curtail.com/downloads, et définissez REGRADE_API_KEY (ou ~/.regrade/key).claude plugin marketplace add https://app.regrade.curtail.com/downloads/latest/marketplace.json
puis claude plugin install regrade@regrade --scope user, et connectez-le une fois (/mcp,
connecté avec le même compte que votre clé).Une petite API "team-chat" (app/store.py) avec des utilisateurs et des canaux. Docker Compose exécute deux copies identiques — même image, même code, pas de drapeau de version :
http://localhost:8001 — vous enregistrez contre celle-ci.http://localhost:8002 — vous rejouez contre celle-ci.Un point de terminaison a un défaut intentionnel :
| Point de terminaison | Comportement |
|---|---|
GET /users/<id> |
Les mots de passe sont hachés en bcrypt au démarrage, donc instance-a et instance-b contiennent des hachages différents pour le même utilisateur.
git clone https://github.com/Curtail-Inc/hello-ReGrade-security
cd hello-ReGrade-security
docker compose up -d --build
traffic/test_api.py est une suite de tests fonctionnels normaux : connexion, lecture d'un utilisateur, renommage d'un utilisateur, liste des canaux. Elle vérifie le comportement CRUD et ne fait aucune assertion de sécurité — elle ne vérifie jamais si le password fuit. (Pourquoi le ferait-elle ? Personne ne savait que le bug existait.)
Démarrez le proxy du capteur devant instance-a :
regrade proxy --target http://localhost:8001 --port 19870
Dans un autre terminal, lancez la même suite de tests — pointez simplement BASE_URL vers le proxy. C'est le seul changement :
BASE_URL=http://localhost:19870 python -m pytest traffic/test_api.py
Tous les tests passent, inchangés. Arrêtez le proxy (Ctrl-C) ; l'enregistrement télécharge et affiche un Recording ID: <uuid> — notez-le.
regrade replay --rec-id <RECORDING_ID> --target http://localhost:8002
Même code des deux côtés — les seules différences sont les valeurs générées par une nouvelle instance.
Ouvrez ce dépôt dans Claude Code et demandez-lui de vous guider à travers la relecture. Guidé par le CLAUDE.md de ce dépôt, il va :
run summarize_deltas → plusieurs deltas : le token de connexion, les horodatages created_at des canaux, et — discrètement parmi eux — $.password.create_id_mapping pour $.token de session (un ID dynamique, pas du bruit à supprimer),create_filter_rule DROP pour $.channels[*].created_at (un horodatage par démarrage),apply_profile_to_replay, puis query_deltas(unlabeled_only=true) — répétez jusqu'à ce qu'un seul delta reste.$.password — un hachage bcrypt dans un corps de réponse. Une fuite de classe CVE, trouvée sans connaissance préalable et sans aucune assertion de sécurité.Le jeton de session et le hachage du mot de passe différent tous les deux entre les deux exécutions — ce sont tous deux des chaînes à haute entropie qui changent à chaque fois. L'un est un bruit légitime que vous cartographiez ; l'autre est une violation. Vous ne pouvez pas les distinguer par « ça a changé » — vous devez regarder ce qui a changé. Filtrer chaque champ à haute entropie comme du bruit aurait caché la vulnérabilité.
Voilà la leçon : ReGrade ne valide pas des attentes, il compare le comportement — il peut donc révéler les bugs auxquels personne n'a pensé à écrire un test.
app/store.py est un seul fichier. Les chemins GET appellent sanitize() ; le chemin PATCH oublie de le faire. Regardez après l'avoir trouvé avec ReGrade — le fait est que ReGrade l'a détecté à partir du trafic seul, en comparant l'application à elle-même.
Apache-2.0.
| Sanitisé — ne renvoie jamais le mot de passe. La référence propre. |
PATCH /users/<id> (rename) | Le bug — renvoie l'objet utilisateur complet incluant le hachage bcrypt password. Un appel sanitize() manquant, exactement comme CVE-2023-5968. |