Aller au contenu principal
own2pwn
Pentest automatisé : ce que les outils prouvent vraiment, sur deux de nos bancs

Pentest automatisé : ce que les outils prouvent vraiment, sur deux de nos bancs

Deux bancs, même règle pour tous, notre moteur compris : Juice Shop en septembre, puis une application qui note elle-même les exploitations, où notre moteur finit derrière ZAP et Wapiti.

Maxime J15 min de lectureMis à jour le

L'automatisation va vite, l'humain tranche

Le module de pentest automatisé de l'EASM tente d'exploiter les findings critiques dans une sandbox bornée. La profondeur reste au pentester.

Voir le pentest automatisé

Un seul soir, une seule cible : OWASP Juice Shop 20.0.0 dans un conteneur, 112 défis de sécurité répartis en 16 catégories. Quatre outils, lancés chacun à leur tour, la cible redémarrée entre chaque passage pour repartir d'un état propre : nuclei, OWASP ZAP, sqlmap, et notre propre moteur d'EASM. On l'écrit d'emblée, c'est le point de l'exercice : l'un de ces outils est le nôtre, on le vend, et on le mesure ici avec la même règle que les autres. Une première version de cet article comptait plus généreusement pour notre moteur que pour ZAP et nuclei ; une contre-expertise l'a relevé, et les chiffres ci-dessous appliquent désormais la même règle à tous, même quand elle nous fait reculer.

Morpheus : what if I told you AI won't replace pentesters
Sur ce banc, aucun des quatre outils n'a appelé de modèle de langage. Ce qui suit mesure de l'automatisation classique.

Sous l'étiquette "pentest IA", beaucoup de pages de vente cachent un scanner de vulnérabilités classique. Sur ce banc, aucun outil n'a utilisé d'IA générative pendant ce passage, le nôtre compris. Le journal de notre moteur ne contient aucun appel à un modèle de langage, et sa commande de lancement n'en configurait aucun. On compare donc quatre formes d'automatisation, pas une IA contre des scanners. La différence de nature entre pentest et scan est détaillée ailleurs, voir pentest vs scan de vulnérabilités. Ici, on compte.

Deux semaines plus tard, on a refait l'exercice autrement : sept configurations de scanners, dont notre moteur, sur une application de notre propre banc qui note elle-même chaque exploitation réussie. Le second banc est décrit plus bas, et il n'est pas plus flatteur pour nous : notre moteur y finit derrière ZAP et Wapiti.

périmètre
défis à résoudrerépartis en 16 catégories de failles
112
outils lancés seulscible redémarrée avant chacun
4
outil authentifiétoute la surface derrière login hors banc
0
Banc own2pwn du 22 septembre 2026, OWASP Juice Shop 20.0.0.

Le protocole, pour que vous puissiez le refaire

Juice Shop expose une route /api/Challenges qui dit, à chaque instant, quels défis sont résolus. C'est notre preuve dure : avant et après chaque outil, on la relit. Un défi qui passe de non résolu à résolu, c'est une faille réellement déclenchée, pas une alerte. La cible est redémarrée (docker restart) avant chaque outil pour qu'aucun ne profite du travail du précédent. Aucun outil n'était connecté. Côté failles hors bande, les conditions n'étaient pas égales : le mode DAST de nuclei a utilisé le serveur Interactsh public (oast.pro), alors que notre moteur a tourné sans son serveur OOB. ZAP et sqlmap n'en avaient pas.

bash
# nuclei : passe standard, puis passe débitée, puis DAST sur les URL crawlées
nuclei -u http://localhost:3000 -jsonl -o nuclei.jsonl
nuclei -u http://localhost:3000 -c 10 -rl 20 -timeout 10 -jsonl -o nuclei-rl.jsonl
katana -u http://localhost:3000 -headless -system-chrome -no-sandbox -jc \
  -d 2 -c 5 -rl 20 -timeout 15 -o urls.txt
nuclei -l urls.txt -dast -c 10 -rl 20 -timeout 10 -jsonl -o nuclei-dast.jsonl

# ZAP : scan passif, puis spider AJAX + scan actif plafonné à 45 min
zap-baseline.py -t http://localhost:3000 -j
zap-full-scan.py -t http://localhost:3000 -j -m 10 \
  -z "-config scanner.maxScanDurationInMins=45 -config scanner.maxRuleDurationInMins=10"

# sqlmap : run CIBLÉ sur le paramètre connu, deux niveaux
sqlmap -u 'http://localhost:3000/rest/products/search?q=test' --batch --level 1
sqlmap -u 'http://localhost:3000/rest/products/search?q=test' --batch --level 3 --risk 2

Les plafonds de temps n'étaient pas les mêmes non plus. Le scan actif de ZAP était borné à 45 minutes. Notre moteur avait un plafond externe de 60 minutes, mais c'est son propre budget interne qui l'a arrêté, au bout d'environ 20 minutes, avec une partie des classes exécutées seulement en partie. nuclei n'avait aucun plafond. Deux lancements ont été écartés : le premier passage de ZAP, contaminé par un crawl qui tournait en parallèle sur la même cible, refait intégralement, et le premier lancement de notre moteur, avorté avant toute requête par son garde-fou réseau.

Ce garde-fou refuse les cibles en boucle locale. Pour ce banc, notre moteur a donc été compilé avec un drapeau de banc d'essai, absent de la version de production, dont le seul effet est d'autoriser une cible en 127.0.0.1. Navigateur activé, sans OOB, sans session de test. Ses scripts de banc ne sont pas publics, mais le résultat se lit à la même aune que les autres : l'API des défis. Versions exactes du jour : nuclei 3.11.1 (templates 10.4.9), ZAP 2.17.0, Juice Shop 20.0.0.

Ce que chaque outil a trouvé

On y distingue trois choses qui ne valent pas pareil : les findings bruts (tout ce que l'outil remonte, bruit compris), les vrais positifs distincts (des failles réelles, vérifiées à la main, dédoublonnées), et les défis significatifs résolus (la preuve dure, en excluant le défi "Error Handling" que n'importe quel HTTP 500 déclenche). Une règle vaut pour tous, le nôtre compris : les expositions informatives ou voulues par conception (CORS permissif, documentation d'API publique, identifiant client OAuth, en-têtes manquants) comptent comme du bruit, pas comme des failles.

résultats
OutilDuréeRequêtesFindings brutsVrais positifsFaux positifsDéfis signif.Catégories / 16
nuclei (débité)14 min20 381191033
OWASP ZAP32 minn.c.19411 (2 au journal)4
sqlmap (ciblé)13 sn.c.11001
notre moteur EASM20 minn.c.84345 (dont 3 fortuits)5
Banc own2pwn du 22 septembre 2026, OWASP Juice Shop 20.0.0. n.c. = non compté. Au journal = défis résolus pendant le passage puis effacés par un plantage de la cible. Catégories couvertes = au moins un vrai positif ou un défi résolu de la catégorie, sur les 16.

Première lecture : combien de catégories de failles, sur les seize, chaque outil a réellement touchées.

couverture
Catégories de failles couvertes, sur 16
Valeurs en catégories
Catégories de failles couvertes, sur 16Notre moteur EASMdont 3 via un défi fortuit5OWASP ZAP4nuclei (débité)3sqlmap (ciblé)outil mono-vulnérabilité, pointé à la main1
Catégories de failles couvertes, sur 16
CatégorieValeur (catégories)Précision
Notre moteur EASM5dont 3 via un défi fortuit
OWASP ZAP4
nuclei (débité)3
sqlmap (ciblé)1outil mono-vulnérabilité, pointé à la main
Banc own2pwn du 22 septembre 2026, OWASP Juice Shop 20.0.0. Une catégorie compte si l'outil a résolu un défi ou remonté un vrai positif vérifié de cette catégorie. Pour notre moteur, trois des cinq catégories ne tiennent qu'à un défi déclenché en passant, par un test qui visait autre chose.

Ensuite le rapport signal sur bruit : à côté des findings bruts, le nombre de vraies failles distinctes qui en sortent.

bruit-signal
Findings bruts contre vrais positifs distincts
  • Findings bruts
  • Vrais positifs distincts
Valeurs en findings
Findings bruts contre vrais positifs distinctsNotre moteur EASM843OWASP ZAP194nuclei (débité)191sqlmap (ciblé)11
Findings bruts contre vrais positifs distincts
CatégorieFindings bruts (findings)Vrais positifs distincts (findings)
Notre moteur EASM843
OWASP ZAP194
nuclei (débité)191
sqlmap (ciblé)11
Banc own2pwn du 22 septembre 2026, OWASP Juice Shop 20.0.0. Attention, l'unité des findings bruts diffère d'un outil à l'autre : nuclei compte des correspondances de templates, ZAP des familles d'alertes (passif et actif, dédoublonnées), notre moteur des findings unitaires, dont 48 findings de dérive (drift) et 3 synthèses de conformité NIS2/CRA qui recomptent les autres. Vrais positifs = failles réelles vérifiées à la main, dédoublonnées, même règle de bruit pour tous.

Quatre-vingt-quatre findings pour trois vraies failles distinctes : notre moteur est le plus bavard du banc, même en tenant compte des unités qui diffèrent. On y revient plus bas, avec ses quatre faux positifs.

Outil par outil, ce que les chiffres cachent

Les réglages par défaut de nuclei ont fait tomber la cible

La toute première passe de nuclei, sans limite de débit, a saturé Juice Shop : le processus Node est tombé en heap out of memory, la cible a cessé de répondre, et nuclei l'a écartée de sa liste après l'avoir vue muette 30 fois. Bilan de cette passe : abandon à 19 %, 3 964 requêtes, zéro finding. Rejouée en débit maîtrisé (-c 10 -rl 20), elle a fini : 20 381 requêtes, 19 correspondances. Sur ces 19, dix-huit sont du repérage (en-têtes manquants, security.txt, Swagger exposé) et une seule est de gravité moyenne : l'endpoint /metrics Prometheus ouvert. Ses trois défis résolus sont /metrics, la politique de sécurité et l'inscription à mot de passe répété, ce dernier déclenché en passant, comme chez notre moteur.

Le DAST de nuclei avait l'URL vulnérable, et n'a rien remonté

On a crawlé l'application avec katana, qui a sorti 52 URL, puis lancé le mode DAST de nuclei dessus : 54 templates, 4 601 connexions HTTP, zéro correspondance. Parmi ces 52 URL figurait pourtant /rest/products/search?q=, celle-là même où ZAP, sqlmap et notre moteur ont trouvé l'injection SQL. Le crawl avait donc atteint l'API et le paramètre ; c'est le passage DAST qui n'a rien signalé sur ce point d'entrée. On n'a pas cherché plus loin pourquoi, et on ne prétend pas le savoir.

ZAP : la vraie injection SQL parmi dix-neuf familles d'alertes, et une cible qui tombe

Entre le scan passif et le scan actif, ZAP a levé dix-neuf familles d'alertes distinctes. Quatre sont de vraies failles, sur quatre catégories : l'injection SQL sur /rest/products/search, la redirection ouverte (remontée sous deux familles, External Redirect et Off-site Redirect, comptée une fois), une bibliothèque JavaScript vulnérable et des fichiers de sauvegarde exposés sous /ftp. Son alerte Cross-Domain Misconfiguration (le CORS permissif) compte comme du bruit, comme chez notre moteur. Un faux positif : une alerte "Source Code Disclosure - File Inclusion" sur /api/Challenges/?sort=name, qui répond une erreur 400 en JSON et ne contient aucun code source.

Le passage de ZAP a surtout été perturbé par la cible elle-même. Pendant le scan actif, Juice Shop a planté trois fois en manque de mémoire (vers 20 h 44, 20 h 57 et 20 h 58), et chaque redémarrage remet la base et les défis à zéro. Or le journal de Juice Shop montre que ZAP a résolu à 20 h 41 le défi de contournement de la liste blanche de redirection, un défi quatre étoiles, le plus difficile résolu de tout ce banc, puis de nouveau le listing de répertoire à 20 h 55. Les deux ont été effacés par les plantages. On s'en tient à l'instantané de fin, qui ne crédite à ZAP qu'un défi, mais ce chiffre sous-estime ce que ZAP a fait ce soir-là.

sqlmap : rien au réglage par défaut, tout au niveau 3

sqlmap est un cas d'école. On l'a pointé à la main sur le bon paramètre (ce n'est pas un scan à l'aveugle), et pourtant, au niveau 1 par défaut, son verdict est tombé : "not injectable". Au niveau 3, risque 2, la même URL est déclarée injectable en boolean, en error et en time-based. La faille n'a pas bougé ; c'est le réglage qui a changé. Sur ce banc, c'est nous qui avons choisi l'URL et monté le curseur : le résultat revient autant à ces deux gestes qu'à l'outil. Le mécanisme de ces injections est détaillé dans les attaques par injection SQL.

Notre moteur : ce qu'il a fait, ce qu'il n'a pas fait, ses faux positifs

Trois vraies failles distinctes : l'injection SQL UNION sur /rest/products/search, l'injection SQL booléenne sur /rest/user/login (avec capture du jeton de l'administrateur, donc le défi de contournement du login admin), et l'endpoint /metrics exposé. Il a aussi remonté le CORS en Access-Control-Allow-Origin: *, la documentation /api-docs/ et l'identifiant client OAuth présent dans main.js ; ce sont des constats exacts, mais de même nature que le CORS de ZAP ou le Swagger de nuclei, donc comptés comme du bruit.

Il a résolu cinq défis significatifs, le plus haut total du banc. Deux seulement viennent d'un test qui les visait : le login admin et /metrics. Les trois autres sont tombés en passant. Le faux avis (forged review) a été déclenché par la sonde de XSS stocké, qui a écrit un canari dans un avis via PUT /rest/products/1/reviews. Le listing de répertoire est venu du crawl, et le mot de passe répété de la création de compte. Ce ne sont pas des failles de contrôle d'accès détectées : le journal du moteur indique que toute sa dimension contrôle d'accès est restée inerte, faute de session de test, sans une seule requête envoyée.

Ensuite ce qu'il n'a pas exécuté : 17 classes de failles web. Treize ont été sautées parce que l'OOB était coupé sur ce banc (les SSRF, Log4Shell, la désérialisation, l'injection XSLT, entre autres), une de contrôle d'accès parce qu'elle exige deux sessions, une de limitation de débit parce que Juice Shop répond 200 à n'importe quel chemin, et deux sont des templates de référence non éligibles. À part, son budget de temps s'est épuisé : une partie des classes lancées n'a rendu que des résultats partiels. L'absence de finding sur tout cela ne prouve rien. Enfin, ses quatre faux positifs :

  • Un mot de passe "en dur" qui n'en est pas un (gravité haute) : le moteur a fait correspondre le mot password dans main.js à un secret codé en dur. C'est un artefact de son expression régulière, pas un vrai secret, et il est remonté en haute gravité. Le pire faux positif du lot.
  • Un finding de conformité NIS2 (gravité haute) qui dérive intégralement du précédent : il hérite donc de son erreur.
  • Un XSS stocké "suspecté" (gravité moyenne) sur la route des avis : le canari ressort dans une réponse JSON d'API, pas dans un contexte HTML. Sans sink HTML, l'exécution n'est pas démontrée.
  • Un upload de fichier "suspecté" non sécurisé (gravité moyenne) sur /upload, une route qui n'existe pas dans Juice Shop : l'application renvoie sa page d'accueil en 200 pour tout chemin inconnu. Le journal du moteur relève lui-même cette route fourre-tout ailleurs, mais le module d'upload a quand même conclu.

Pourquoi on publie nos propres faux positifs

Un banc où le vendeur se note lui-même ne vaut rien s'il masque ses ratés. Notre moteur a résolu le plus de défis et couvert une catégorie de plus que ZAP, mais ZAP a sorti plus de vraies failles (quatre contre trois) avec moins de faux positifs (un contre quatre). Pour un acheteur, les 84 findings se réduisent à trois vraies failles et quatre faux positifs à trier.

Second banc : 27 failles notées par l'application, notre moteur derrière ZAP et Wapiti

Le défaut du banc Juice Shop, c'est qu'il fallait vérifier chaque finding à la main. Le 7 octobre 2026, on a donc pris une autre cible, une application PHP rendue côté serveur de notre banc ptaas-bench, qui porte 27 failles plantées. Chacune a un capteur dans le code : l'application enregistre elle-même l'effet d'une exploitation réussie, et c'est ce signal, jamais le rapport de l'outil, qui crédite une faille. Sept configurations, 600 secondes chacune, la même session client ouverte pour toutes : ZAP passif et actif, Wapiti 3.3.2, nuclei 3.11.1 par défaut et en -dast, Nikto 2.6.1 et notre moteur en mode pentest. Aucun outil n'avait le compte administrateur.

Le banc est le nôtre, et notre moteur a été réglé contre lui

Précision nécessaire : ptaas-bench est écrit par own2pwn, et notre moteur a été mis au point en le regardant, avec des passages sur cette même cible les 10 et 12 septembre. Il finit quand même derrière ZAP actif et Wapiti, et ne prouve aucune faille qu'un autre outil n'ait aussi prouvée. Ses trois injections prouvées tombent dans les 20 dernières secondes du budget de 600 s : notre moteur n'est pas réglé pour un temps aussi court, mais c'est la règle que nous avons fixée pour tous. Aucun composant de ce banc n'appelle de modèle de langage : ce n'est pas une mesure de "pentest IA".
notre moteur
failles prouvées sur 27, selon l'exécutioncontre 12 pour ZAP actif et 8 pour Wapiti
4 à 6
arrivée de ses trois injections prouvéessur une fenêtre fermée à 601,5 s
581 à 595 s
faille prouvée par lui seulchacune de ses preuves est aussi celle d'un autre outil
0
Banc ptaas-bench d'own2pwn (commit 85f8f61), cible legacy, mesure du 7 octobre 2026, contre-expertise du 8 octobre. Fenêtre de mesure de l'exécution retenue : 601,5 s. Autre exécution du même moteur, à trois comptes : 4 failles, dont 1 injection à 583 s.

Le classement complet : ZAP actif 12 failles prouvées, Wapiti 8, notre moteur 6 à l'exécution retenue (4 lors d'une autre), ZAP passif 3, nuclei avec ses templates par défaut 1, Nikto 0. nuclei en -dast, lancé sans liste d'URL à paramètres, n'a envoyé que 5 requêtes : une configuration inopérante, pas une mesure de l'outil. Si l'on ne garde que les 19 failles dont la preuve est l'exploitation elle-même, les injections au sens large, on obtient 8 pour ZAP actif, 4 pour Wapiti, 3 pour notre moteur et 1 pour nuclei. L'écart entre nos deux exécutions ne vient pas du nombre de comptes, mais de la coupure : tout se joue dans les dernières secondes. Le tableau ci-dessous dit qui a prouvé quoi.

qui prouve quoi
Faille plantéeRouteCompte requisProuvée par
Injection SQL aveugle/product.phpsans compteZAP actif, notre moteur
Injection SQL aveugle/branch.phpsans compteZAP actif, Wapiti, notre moteur
Injection SQL aveugle/account/orders.phpclientpersonne
Inclusion de fichier local/help.phpsans compteZAP actif
Inclusion de fichier local/sans comptepersonne
Inclusion de fichier distant/admin/widget.phpadminpersonne
Traversée de chemin/download.phpsans compteZAP actif, notre moteur
Traversée de chemin/account/invoice.phpclientpersonne
Dépôt de fichier sans contrôle/account/documents.phpclientpersonne
Désérialisation PHP/sans comptepersonne
Désérialisation PHP/cart.phpsans comptepersonne
Injection de commande/admin/tools.phpadminpersonne
Injection de commande/account/documents.phpclientWapiti
Injection d'en-tête de mail/contact.phpsans compteZAP actif, Wapiti
Injection CSV/account/quote.phpclientpersonne
Injection CSV/contact.phpsans comptepersonne
XSS réfléchie/search.phpsans compteZAP actif, nuclei (défaut)
XSS réfléchie/account/login.phpsans compteZAP actif
XSS réfléchie/stock.phpsans compteZAP actif, Wapiti
Hachage faible du mot de passe/account/profile.phpclientpersonne
Mot de passe en clair dans le cookie de connexion persistante/account/login.phpsans compteWapiti
Drapeaux de cookie manquants/account/login.phpsans compteZAP actif, Wapiti, notre moteur, ZAP passif
Énumération de comptes/account/login.phpsans compteZAP actif, notre moteur
Énumération de comptes/account/reset.phpsans compteZAP actif, Wapiti, ZAP passif
Fixation de session/account/login.phpsans comptepersonne
Script tiers sans SRI/quote.phpsans compteZAP actif, Wapiti, notre moteur, ZAP passif
Inclusion de fichier local/admin/reports.phpadminpersonne
Banc ptaas-bench d'own2pwn (commit 85f8f61), cible legacy (PHP/Apache, rendu côté serveur, 27 failles plantées), une analyse par outil, connectée avec un seul compte client, budget de 600 s, mesure du 7 octobre 2026. Une faille ne compte comme prouvée que si l'application a enregistré elle-même l'effet de l'exploitation. Notre moteur : exécution retenue, un seul compte. nuclei -dast et Nikto n'ont rien prouvé.

Trois choses sautent aux yeux. La colonne "compte requis" : sur les neuf lignes "client" ou "admin", une seule a été prouvée, par Wapiti, alors que tous les outils avaient une session client valide. Les trois lignes "admin" étaient hors d'atteinte par construction ; les six lignes "client" ne l'étaient pas. Ensuite, les failles les plus partagées sont les plus faciles : drapeaux de cookie et script tiers sans contrôle d'intégrité se prouvent sans attaque, en se connectant ou en affichant la page, et quatre configurations les ont. Enfin, treize lignes "personne" : désérialisations, injections CSV, dépôt de fichier, hachage faible, fixation de session, entre autres. Ce sont exactement les tests qu'un pentester mène à la main, avec une idée de ce que l'application est censée faire.

Deux défauts de notre outillage, à verser au dossier. Le rapport de notre moteur n'a pas pu être récupéré : le client du pilote de banc refusait un message de 4,24 Mo, au-delà de sa limite par défaut de 4 Mo (le chemin de production accepte bien plus). Ses faux positifs ne sont donc pas mesurés sur ce banc. Et les budgets n'étaient pas strictement égaux : une grâce de 90 s pour les scanners tiers contre 30 s pour notre moteur, ce qui le défavorise, mais ne change rien pour Wapiti, dont toutes les preuves arrivent avant 91 s. La méthode complète, les graphiques par configuration et les autres limites sont dans notre article sur le DAST mesuré sur sept configurations.

Ce que l'automatisation a fait ici, et ce qu'elle n'a pas fait

Sur les classes qu'ils connaissent, les outils ont été rapides et bon marché. L'injection SQL de /rest/products/search, trois outils sur quatre l'ont trouvée ; le mode DAST de nuclei l'avait dans sa liste et l'a manquée. Trois limites ressortent de ces bancs :

  • L'authentification : sur Juice Shop, aucun outil n'était connecté, donc toute la surface derrière un login est restée non testée. Sur le banc d'octobre, tous l'étaient, et ils n'ont prouvé qu'une des six failles du compte client. Avoir une session ne suffit pas à s'en servir.
  • La logique métier : le seul défi de contrôle d'accès résolu sur ce banc (le faux avis) l'a été par effet de bord d'une sonde XSS. Aucun outil n'a testé qu'un client ne devrait pas consulter le panier d'un autre ou valider sa propre commande.
  • Le chaînage : aucun des quatre n'a enchaîné deux failles en une compromission. Et les classes qui exigent un rappel hors bande n'ont pas été jouées par notre moteur, parce que son OOB était coupé ce soir-là, ce qui ne dit rien de ce qu'il donne avec.

C'est pour ces raisons que notre pentest automatisé est vendu pour ce qu'il est : une couverture continue entre deux missions humaines, qui tente d'exploiter les findings critiques dans une sandbox bornée, pas un remplacement du pentest web blackbox où un opérateur prend la main sur l'authentification, la logique métier et le chaînage. Le SAST assisté par IA joue un rôle voisin plus tôt, dès le commit.

Les 3 questions qui démasquent un faux pentest IA

  • Y a-t-il exploitation réelle ? Une preuve qui déclenche la faille (un défi résolu, un token capturé), ou une alerte "potentielle" ?
  • Qui valide les findings ? Un mécanisme déterministe, un humain, ou personne ? Sur ce banc, notre moteur a sorti quatre faux positifs qu'un tri humain écarte.
  • La logique métier est-elle testée ? Si la réponse est floue, c'est un scan, pas un pentest.

Ce que ces bancs disent, et ce qu'ils ne disent pas

Sur Juice Shop, le 22 septembre, notre moteur a résolu plus de défis que les autres (cinq, dont trois en passant) et couvert une catégorie de plus que ZAP. ZAP, lui, a sorti plus de vraies failles avec moins de faux positifs, et le seul défi quatre étoiles de la soirée, effacé ensuite par une cible qui tombait. nuclei a été propre mais court, sqlmap juste une fois bien réglé. C'est un passage par outil, sur une application, avec des plafonds et des conditions OOB différents : une mesure, pas un classement.

Sur le banc d'octobre, la règle est plus dure, puisque c'est l'application qui note, et le résultat est net : ZAP actif 12, Wapiti 8, notre moteur 4 à 6. Ce banc a ses propres limites : une seule application PHP rendue côté serveur, le cas le plus facile pour un crawler, donc rien sur les applications à page unique ni sur les API ; une exécution par outil, sauf Wapiti et ZAP passif, rejoués avec le même résultat ; et un banc écrit par nous, contre lequel notre moteur a été réglé. Que notre moteur reste derrière ZAP actif et Wapiti malgré ce biais en sa faveur rend le classement robuste dans ce sens-là, pas dans l'autre.

La cible et trois des quatre outils sont publics, les commandes sont plus haut. Si vous refaites la mesure et tombez sur d'autres chiffres, on veut les voir : c'est pour ça qu'on la publie brute. Le banc d'octobre est public lui aussi, cibles comprises.

À retenir

  • Sur Juice Shop (22 septembre), notre moteur a résolu le plus de défis, mais ZAP a sorti plus de vraies failles (4 contre 3) avec moins de faux positifs (1 contre 4).
  • Sur le banc d'octobre, noté par l'application, ZAP actif prouve 12 failles sur 27, Wapiti 8, notre moteur 4 à 6, alors que le banc est le nôtre et que notre moteur a été réglé contre lui.
  • À sept configurations, 14 failles sur 27 sont prouvées, et une seule des 9 qui demandent d'être connecté.
  • Aucun outil de ces deux bancs n'a appelé de modèle de langage. Méfiez-vous des chiffres de "pentest IA" qui ne disent pas comment ils comptent.
  • L'automatisation couvre vite les classes connues ; l'authentification, la logique métier et le chaînage restent à un pentest mené par un humain.

Questions fréquentes

Quel outil de pentest automatisé prouve le plus de failles ?

Sur notre banc du 7 octobre 2026 (une application PHP volontairement vulnérable, 27 failles plantées, 600 secondes et le même compte client pour tous, l'application notant elle-même les exploitations), ZAP en mode actif fait prouver 12 failles, Wapiti 8 et notre moteur 4 à 6 selon l'exécution. Le banc est le nôtre et notre moteur a été réglé contre lui en septembre : il finit quand même dernier des trois scanners actifs et ne prouve aucune faille qu'un autre outil n'ait aussi prouvée.

Un scanner automatisé trouve-t-il l'injection SQL de Juice Shop ?

Sur ce banc, trois outils sur quatre l'ont trouvée. sqlmap pointé sur le bon paramètre a répondu 'non injectable' au niveau 1 par défaut, puis a confirmé une injection boolean, error et time-based au niveau 3 risque 2. ZAP l'a signalée parmi dix-neuf familles d'alertes. Notre moteur l'a confirmée et a rejoué le contournement du login admin. Le mode DAST de nuclei avait l'URL /rest/products/search?q= dans sa liste et n'a rien remonté.

Un pentest automatisé remplace-t-il un pentest humain ?

Pas sur nos bancs. Sur Juice Shop, aucun outil n'était authentifié et aucun n'a enchaîné deux failles. Sur le banc d'octobre, tous avaient une session client valide, et à eux sept ils n'ont prouvé qu'une des 9 failles qui demandent d'être connecté, et 14 failles sur 27 au total. L'automatisation apporte du volume sur les classes qu'elle connaît, pas la profondeur d'une mission.

Jusqu'où ces résultats se généralisent-ils ?

Peu. Pour Juice Shop, c'est un seul passage par outil, sur une seule application volontairement vulnérable, sans authentification, sans serveur OOB pour notre moteur, avec des plafonds de temps différents et une cible qui a planté trois fois pendant le scan actif de ZAP. Un autre réglage, une autre application ou un second passage peuvent inverser l'ordre. Le banc d'octobre a les mêmes limites : une seule application PHP, rien sur les SPA ni les API. Ce sont des mesures, pas un classement.

Peut-on reproduire ce benchmark ?

Oui. La cible est publique (OWASP Juice Shop 20.0.0), les outils aussi (nuclei, katana, ZAP, sqlmap), et les commandes exactes sont dans l'article. L'état résolu se lit sur l'API /api/Challenges de Juice Shop. Seul notre moteur n'est pas open source ; le reste se rejoue à l'identique.

Veille sécurité

La suite, une fois par mois

Ce qui bouge vraiment sur la surface d'attaque externe, NIS2 et la sécurité applicative, écrit par le pentester qui signe ces articles. Un envoi par mois, désinscription en un clic.

Votre adresse ne sert qu'à cet envoi. Voir la politique de confidentialité.