Contact

RZ-2026-001

Publié
Type
Note de recherche
Publié le
Mis à jour
Durée
8 min de lecture
Auteur
Équipe rootzero

Preuve avant alerte : publier une vulnérabilité seulement quand elle est démontrée

Note de méthode : hypothèse, tentative, réponse observée, validation humaine, re-test rejouable. Un constat sans preuve reste une alerte à qualifier.

  • Méthodologie
  • Pentest
  • Vulnérabilités

Résumé

Les rapports de test d’intrusion mêlent souvent constats démontrés et inférences : versions supposées vulnérables, signatures reconnues, réponses ambiguës. Cette note définit ce qu’est un constat démontré et propose une chaîne en cinq étapes (hypothèse, tentative, réponse observée, validation humaine, re-test rejouable), illustrée par la détection d’une injection de template côté serveur. Elle recense les principales classes de faux positifs et les limites de l’approche.

Référence
RZ-2026-001
Statut
Publié
Publication
Révision
Auteur
Équipe rootzero
Lecture
8 min de lecture

1. Problème

Un rapport de sécurité sert à prendre des décisions, et chaque ligne qu’il contient consomme du temps : lecture, qualification, arbitrage, correction, vérification. Quand il mélange des vulnérabilités démontrées et des vulnérabilités possibles, il laisse au lecteur le soin de les distinguer, alors que l’auteur était le mieux placé pour le faire.

Ce mélange est courant. Les outils d’analyse automatisée s’appuient en grande partie sur des inférences : une bannière de version, une signature reconnue dans une réponse, un délai inhabituel. Ces indices signalent une possibilité qui reste à établir. Le projet OWASP Benchmark a été créé pour mesurer, sur une application volontairement vulnérable, la proportion de vrais et de faux positifs produits par ces outils.

Cette note pose une règle et en tire les conséquences : un constat n’est publié que s’il est démontré. Les autres observations sont des pistes de test. Elles peuvent figurer au rapport, dans une section distincte et sous ce nom.

2. Définition du constat démontré

Nous appelons constat démontré un constat qui remplit les cinq conditions suivantes.

  1. Il énonce une hypothèse explicite, avec le défaut supposé et son mécanisme : « le paramètre name est interprété par un moteur de templates côté serveur ».
  2. Il repose sur une tentative minimale, c’est-à-dire une requête ou une courte séquence de requêtes conçue pour valider l’hypothèse avec le moins d’effets possible sur la cible.
  3. La réponse observée est discriminante. Elle diffère d’une réponse de référence (baseline), et cette différence ne s’explique raisonnablement que par l’hypothèse.
  4. Une personne compétente, distincte de l’outil qui a produit la tentative, a relu la paire requête/réponse et confirmé l’interprétation.
  5. La tentative est enregistrée avec ses préconditions et peut être rejouée à l’identique, par le client pour vérifier ou après correction pour confirmer.

La troisième condition est la plus exigeante. Elle écarte les constats fondés uniquement sur un numéro de version, sur la présence d’un mot-clé dans une page d’erreur ou sur un signal statistique faible.

3. Méthode : la chaîne en cinq étapes

Chaque étape produit un artefact conservé avec le constat.

Étape Question Artefact
Hypothèse Quel défaut, à quel endroit, par quel mécanisme ? Énoncé, point d’entrée, classe (CWE)
Tentative Quelle est la plus petite action qui la valide ? Requêtes brutes, horodatées
Réponse observée Qu’est-ce qui diffère de la référence ? Réponse de référence, réponse de test, discriminant identifié
Validation humaine L’interprétation tient-elle ? Relecteur, décision, commentaire
Re-test La preuve se rejoue-t-elle, et disparaît-elle après correction ? Enregistrement rejouable, résultat daté

3.1 Exemple canonique : injection de template côté serveur

L’exemple suivant est fictif et le domaine app.exemple.fr n’existe pas. Il reprend une détection classique d’injection de template côté serveur (SSTI), technique décrite par James Kettle en 2015 dans Server-Side Template Injection et intégrée au guide de test OWASP sous la référence WSTG-INJT-18.

L’hypothèse porte sur la page d’aperçu /preview, qui insérerait le paramètre name dans un template rendu côté serveur sans le traiter comme une simple donnée.

On établit d’abord la référence, c’est-à-dire le comportement normal :

GET /preview?name=rootzero HTTP/1.1
Host: app.exemple.fr
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

<p class="greeting">Bonjour rootzero</p>

La tentative soumet ensuite une expression arithmétique dans la syntaxe des moteurs les plus courants, {{7*8}}, encodée dans l’URL :

GET /preview?name=%7B%7B7*8%7D%7D HTTP/1.1
Host: app.exemple.fr
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8

<p class="greeting">Bonjour 56</p>

La chaîne {{7*8}} n’apparaît pas dans la réponse. Elle a été remplacée par 56, le résultat du calcul, ce qui montre que le serveur a évalué l’entrée.

Trois précautions rendent ce discriminant fiable.

  • Le nombre 56 ne doit apparaître nulle part ailleurs dans la page de référence, faute de quoi sa présence ne prouverait rien. C’est pourquoi on choisit un produit peu courant plutôt qu’un nombre rond.
  • Une seconde tentative avec un autre opérande, {{9*9}} qui doit donner 81, écarte la coïncidence et confirme qu’il s’agit d’un calcul.
  • L’expression {{7*'8'}} permet d’identifier le moteur sans aggraver le test : Jinja2 répète la chaîne et renvoie 8888888, Twig renvoie 56. Connaître le moteur suffit à qualifier l’impact potentiel, documenté publiquement pour chacun d’eux.

La preuve s’arrête là. Pousser jusqu’à l’exécution de commandes sur le serveur n’ajouterait rien à la démonstration du défaut et ferait courir un risque supplémentaire à la cible. Une escalade n’est envisagée que si le périmètre autorisé la prévoit explicitement, et elle reste alors minimale.

Le relecteur vérifie enfin la référence, les deux tentatives et l’absence d’explication concurrente (cache, réponse générique, contenu dynamique qui contiendrait le nombre), puis consigne sa décision.

3.2 L’enregistrement rejouable

Le constat est conservé dans un format qui permet de le rejouer mécaniquement, par exemple :

id: F-0007
title: Injection de template côté serveur sur /preview (paramètre name)
cwe: CWE-1336
target: https://app.exemple.fr        # exemple fictif
preconditions:
  auth: none
steps:
  - name: baseline
    request: { method: GET, path: "/preview", query: { name: "rootzero" } }
    expect: { status: 200, body_not_contains: "56" }
  - name: proof
    request: { method: GET, path: "/preview", query: { name: "{{7*8}}" } }
    expect: { status: 200, body_contains: "Bonjour 56", body_not_contains: "{{7*8}}" }
  - name: control
    request: { method: GET, path: "/preview", query: { name: "{{9*9}}" } }
    expect: { status: 200, body_contains: "Bonjour 81" }
validated_by: "relecteur (initiales)"
validated_at: 2026-09-08

Après correction, le re-test exécute les mêmes étapes. Le rapport doit distinguer trois issues. Si la référence passe et que la preuve échoue (l’entrée est rendue littéralement ou rejetée), le défaut est corrigé. Si la preuve passe encore, il ne l’est pas. Si la référence elle-même échoue parce que le point d’entrée a été supprimé, l’authentification modifiée ou la page déplacée, le résultat est non concluant, et le rapport ne doit en aucun cas le présenter comme une correction.

4. Classes de faux positifs

L’exigence de démonstration écarte par construction plusieurs familles de faux positifs fréquentes dans les rapports automatisés.

4.1 Inférence par version

Une bannière annonce une version concernée par une CVE. Les distributions Linux corrigent pourtant souvent les vulnérabilités par rétroportage, sans changer le numéro de version amont : le paquet paraît vulnérable d’après la bannière et il est corrigé en réalité. Le cas inverse existe aussi, avec des bannières masquées ou falsifiées.

4.2 Réflexion prise pour une exécution

La charge utile revient telle quelle dans la réponse, dans un message d’erreur ou un champ de formulaire pré-rempli. Sa présence ne prouve pas qu’elle a été interprétée. Pour cette raison, le discriminant de l’exemple 3.1 repose sur le résultat (56) et sur l’absence de l’entrée d’origine.

4.3 Réponses génériques

Pare-feu applicatifs, pages d’erreur uniformes et redirections systématiques renvoient la même réponse à toute entrée jugée suspecte. Cette réponse peut correspondre à une signature sans révéler aucun défaut.

4.4 Mesures temporelles bruitées

Les injections dites aveugles s’appuient souvent sur un délai. La gigue réseau, la charge du serveur ou un cache produisent des délais du même ordre. Une preuve temporelle demande donc des mesures répétées et une relation dose-effet : des délais demandés de 0, 3 puis 6 secondes doivent produire des temps de réponse qui suivent cette progression, de façon reproductible.

4.5 Interactions hors bande parasites

Un test de falsification de requête côté serveur (SSRF) attend que la cible contacte une adresse contrôlée par le testeur. D’autres systèmes le font parfois à sa place : robots d’aperçu de liens, passerelles de sécurité de messagerie, scanners qui visitent les URL reçues. Il faut attribuer la connexion entrante à l’application avant de conclure.

4.6 Caches et doublons

Une réponse servie par un CDN ou un cache intermédiaire peut provenir d’une requête antérieure, y compris d’un autre test. Les doublons, enfin, comptent plusieurs fois un même défaut atteint par plusieurs chemins. Ils ne sont pas des faux positifs au sens strict, et ils faussent quand même l’appréciation du volume.

5. Origines de la sur-déclaration

La sur-déclaration touche l’ensemble de la catégorie et tient à la position des outils. Pour un éditeur, manquer une vulnérabilité réelle est plus visible et plus coûteux que signaler une vulnérabilité inexistante, et les seuils de détection se règlent en conséquence. Un outil générique ignore la logique métier, la configuration effective et les mesures compensatoires. Beaucoup de détections comparent une réponse à une signature au lieu de la comparer à une réponse de référence de la même application. Et sans relecture humaine, rien n’arrête une interprétation erronée avant le rapport.

L’apprentissage automatique ne règle pas ces points à lui seul. Un modèle peut formuler une hypothèse plausible, et cette plausibilité reste à démontrer comme n’importe quelle autre. La chaîne décrite ici s’applique quelle que soit la nature de l’outil qui produit la tentative.

6. Limites

Certaines classes de vulnérabilités ne se démontrent pas sans dommage : déni de service, conditions de course à effet durable, défauts de logique métier qui impliquent de vraies transactions. Pour celles-ci, nous recommandons de documenter l’hypothèse et les indices comme piste non démontrée, dans une section séparée des constats, et de proposer un test dans un environnement dédié.

L’absence de constat démontré ne prouve pas l’absence de vulnérabilité. Un rapport fondé sur la preuve doit donc expliciter sa couverture, en indiquant ce qui a été testé et ce qui ne l’a pas été.

La validation humaine a un coût. Elle limite le volume traitable et dépend de la compétence du relecteur. Ce coût est assumé, puisqu’il passe du lecteur du rapport à son auteur.

La preuve doit rester minimale. Démontrer un accès à des données ne nécessite pas de les extraire. Une preuve qui collecte des données personnelles au-delà du nécessaire pose un problème de conformité au RGPD, indépendamment de sa valeur technique. Un identifiant d’enregistrement, un nombre de lignes ou une valeur masquée suffisent en général.

Enfin, aucune tentative n’est légitime hors d’un périmètre autorisé par écrit. La note RZ-2026-002 traite ce point pour les agents autonomes.

7. Mise en œuvre

SpectrAI fonctionne selon cette chaîne. Les agents formulent des hypothèses et exécutent des tentatives dans le périmètre déclaré. Un constat n’est publié qu’avec la paire requête/réponse qui le démontre et après relecture humaine, et les re-tests rejouent la preuve enregistrée. La méthode a d’abord été formulée pour des audits conduits à la main et s’applique aussi bien à un test d’intrusion manuel.

Références

Citer cette note

Équipe rootzero. « Preuve avant alerte : publier une vulnérabilité seulement quand elle est démontrée ». Note de recherche RZ-2026-001, rootzero, 10 septembre 2026. https://rootzero.eu/research/preuve-avant-alerte/

#Méthode appliquée

Chaque constat publié se rejoue.

SpectrAI applique ces principes au pentest web et API : périmètre déclaré, constats démontrés, validation humaine, re-tests rejouables.