Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

La carte et la fenêtre : comment un scan agentique a transformé une fuite de documentation en identifiants volés

Découvrez comment Agentic Deep Scan d'Ostorlab a enchaîné une divulgation OpenAPI de sévérité moyenne et une vulnérabilité SSRF pour contourner une restriction de loopback et extraire des identifiants de base de données.

Ostorlab Agentic Deep Scan a trouvé une spécification OpenAPI lisible publiquement à l'adresse /static/openapi.json et l'a classée en sévérité moyenne : de la documentation, sans identifiants. Il a ensuite lu ce document comme un inventaire et l'a parcouru jusqu'à deux vulnérabilités critiques, un jeu d'identifiants de base de données valides et une session d'administrateur falsifiée.

Qu'est-ce qu'un chemin d'attaque inter-couches ? Un chemin d'attaque inter-couches est un chemin où un artefact exposé à un niveau d'une application — un fichier de build, une ressource statique, un bundle client — fournit ce qu'il faut pour atteindre une faiblesse située à un autre niveau, généralement le backend. L'artefact n'est souvent pas une vulnérabilité en soi ; sa valeur est de supprimer les tâtonnements lors du test d'autre chose.

Résumé (TL;DR)

Deux endpoints de cette spécification ont compté. /internal/secret renvoie les secrets de l'application et refuse tout le monde sauf le serveur lui-même, en répondant aux requêtes directes par HTTP 403 Internal resource. Loopback only. /upload_profile_picture_url accepte une URL et la récupère, côté serveur.

Pointer le second vers le premier a satisfait la restriction de loopback au lieu de la briser. La réponse contenait les clés de signature de l'application, des secrets JWT et des identifiants de base de données — et la clé de signature divulguée a ensuite servi à forger un jeton d'administrateur accepté par l'application.

Environnement de test et méthodologie

Cette présentation décrit un exercice de benchmark simulé et autorisé. Tous les tests ont été réalisés par Ostorlab Agentic Deep Scan contre vulnbank.org, une application bancaire volontairement vulnérable, publiée et maintenue pour la recherche en sécurité et l'évaluation d'outils. Aucun système de production, aucune donnée client réelle et aucun actif tiers n'ont été impliqués, et chaque identifiant présenté ci-dessous est une valeur de test pré-remplie appartenant à ce bac à sable.

Pourquoi les scanners s'arrêtent à la spécification

Les outils fondés sur des signatures évaluent une requête à la fois par rapport à une bibliothèque de motifs malveillants connus. Cette primitive est rapide et déterministe, et elle présente trois angles morts structurels qu'aucune règle supplémentaire ne comble.

Limite Pourquoi elle existe Ce qu'elle manque ici
Aucun état entre les requêtes Les endpoints sont évalués isolément, requête par requête Que deux endpoints listés dans le même fichier se combinent en un contournement
Aucune notion de finalité Les règles encodent la syntaxe, pas ce à quoi sert une fonctionnalité par rapport à ce qu'elle fait Qu'un outil de téléversement de photo de profil est, du point de vue du serveur, un générateur de requêtes
Un contrôle qui fonctionne met fin au test Un 403 correct est enregistré comme un résultat négatif et l'endpoint est abandonné Que le contrôle désigne une partie autorisée qu'il serait peut-être possible d'atteindre par d'autres moyens

Chacun de ces comportements est correct pris isolément. Récupérer la spécification ne produit aucune correspondance de signature. Sonder /internal/secret produit un véritable 403. Aucune de ces observations n'est fausse, et aucune ne produit de vulnérabilité.

Ce que fait l'agent différemment

Agentic Deep Scan d'Ostorlab exécute une boucle de raisonnement continue en cinq étapes pour découvrir et valider les vulnérabilités :

  1. Reconnaissance — cartographier la surface : endpoints, paramètres, exigences d'authentification, artefacts exposés.
  2. Hypothèse — raisonner sur ce qui pourrait mal tourner au vu de ce qui a été observé.
  3. Test — envoyer la requête qui confirme ou infirme l'hypothèse.
  4. Validation — confirmer un impact réel plutôt qu'une réponse suggestive.
  5. Enchaînement — se demander si ce résultat, combiné à tout ce qui a déjà été trouvé, ouvre un chemin encore inexploré.

Les étapes 2 et 5 font la différence. Un scanner fondé sur des signatures dispose de l'étape 3 et, faiblement, de l'étape 4. Il ne formule aucune hypothèse sur la finalité d'une fonctionnalité et ne conserve aucun modèle des vulnérabilités précédentes à combiner avec la vulnérabilité courante. Un agent tient un inventaire continu de l'application — ce qu'il a vu, ce qu'il a écarté, ce qui reste inexpliqué — et le consulte à chaque nouveau résultat. La chaîne ci-dessous existe tout entière à l'étape 5.

Voir la chaîne à l'œuvre : un enchaînement trouvé par Agentic Deep Scan

Vulnérabilité n° 1 (moyenne) : spécification OpenAPI accessible publiquement

L'application servait un document OpenAPI 3.0 complet depuis son répertoire public de ressources statiques :

$ curl -s https://vulnbank.org/static/openapi.json | wc -l
1471

$ curl -I https://vulnbank.org/static/openapi.json
HTTP/2 200
content-type: application/json

Aucune authentification, un chemin prévisible, 1 471 lignes décrivant 32 endpoints avec leurs schémas et les exigences d'authentification de chaque route. /static/ est l'endroit où un frontend range ses feuilles de style et ses images — pas un contrat de backend.

Vue de la vulnérabilité dans Ostorlab pour « OpenAPI Specification Publicly Accessible - Information Disclosure », classée en sévérité moyenne et associée au ticket ostoraa-26070, montrant la cause racine à l'adresse /static/openapi.json et une commande curl qui renvoie 1 471 lignes de spécification d'API.
La vulnérabilité initiale : une spécification d'API lisible publiquement

Figure 1 : classée en sévérité moyenne, à juste titre en elle-même. La description de la vulnérabilité signale déjà la conséquence : la spécification « reveals the existence of sensitive endpoints like /sup3r_s3cr3t_admin that would otherwise require extensive crawling to discover. »

Son énumération a transformé 1 471 lignes en une surface structurée — y compris des routes que l'application ne référence jamais :

Catégorie Endpoints
Authentification /login, /register, /api/v1/forgot-password
Transactions /transfer, /transactions/{account_number}, /check_balance
Administration /sup3r_s3cr3t_admin, /admin/create_admin, /admin/delete_account/{user_id}
Interne /internal/config.json, /internal/secret, /latest/meta-data/*
Téléversement de fichiers /upload_profile_picture, /upload_profile_picture_url

Preuve d'exploitation issue de la vulnérabilité OpenAPI, montrant l'énumération avec jq de .paths et .components.schemas, un scénario d'attaque allant de la découverte des endpoints jusqu'à l'introspection GraphQL, et une preuve de validation confirmant que 1 471 lignes sont accessibles sans authentification.
La spécification analysée sous forme d'inventaire des endpoints

Figure 2 : la spécification analysée sous forme d'inventaire. La valeur ne tient pas à un chemin en particulier — mais au fait de tenir toute la surface sous les yeux en même temps.

L'hypothèse évidente, réfutée à juste titre. /internal/secret s'annonce lui-même, donc l'agent l'a demandé :

$ curl -s -i https://vulnbank.org/internal/secret
HTTP/2 403
{"error": "Internal resource. Loopback only."}

Refusé, et refusé à raison. L'endpoint vérifie d'où vient la requête et ne répond qu'à la machine elle-même. Pour un outil qui teste les endpoints indépendamment, c'est la fin du chemin : hypothèse réfutée, contrôle solide, on passe à autre chose.

Vulnérabilité n° 2 (critique) : SSRF via le téléversement de photo de profil

La bonne question n'était pas de savoir comment vaincre le contrôle de loopback, mais qui le satisfait — et si cette partie peut être amenée à agir.

Loopback désigne le serveur. Une ligne de l'inventaire, rangée sous le téléversement de fichiers, décrit une fonctionnalité qui fait émettre des requêtes sortantes au serveur à la demande :

$ curl -X POST https://vulnbank.org/upload_profile_picture_url \
    -H "Authorization: Bearer <JWT>" \
    -H "Content-Type: application/json" \
    -d '{"image_url": "http://127.0.0.1:5000/internal/secret"}'

La requête provient désormais de 127.0.0.1. Le contrôle de loopback est franchi — réellement satisfait, pas contourné.

Vue de la vulnérabilité dans Ostorlab pour « SSRF via Profile Picture Upload Allows Internal Resource Access and Sensitive Data Exfiltration », classée en sévérité critique et associée au ticket ostoraa-26090, dont le bloc de code vulnérable est annoté « # From OpenAPI spec analysis ».
Le pivot, retracé jusqu'à la spécification

Figure 3 : la vulnérabilité consigne elle-même sa provenance dans la première ligne de son bloc de code — # From OpenAPI spec analysis.

{{
  "secrets": {{
    "app_secret_key": "secret123",
    "jwt_secret": "secret123",
    "env_preview": {{
      "DB_HOST": "db",
      "DB_NAME": "vulnerable_bank",
      "DB_USER": "postgres",
      "DB_PASSWORD": "postgres"
    }},
    "DEEPSEEK_API_KEY": "sk-e2719..."
  }}
}}

Preuve d'exploitation issue de la vulnérabilité SSRF : l'enregistrement d'un compte de test, le POST vers /upload_profile_picture_url avec image_url http://127.0.0.1:5000/internal/secret, et le payload exfiltré reproduit tel quel, contenant app_secret_key, jwt_secret, des identifiants de base de données et une clé d'API tierce.
Trois requêtes, de l'inscription aux identifiants

Figure 4 : la séquence complète telle qu'enregistrée — inscription, pivot, exfiltration. Le payload est capturé tel quel plutôt que résumé.

Vulnérabilité n° 3 (critique) : exposition de données sensibles via /internal/secret non protégé

Une seule réponse suggestive ne constitue pas une vulnérabilité ; le chemin a donc été rejoué pour confirmer que le comportement était reproductible et que les valeurs étaient réelles :

Tentative de validation 1 — La requête SSRF via /upload_profile_picture_url vers http://127.0.0.1:5000/internal/secret a renvoyé l'intégralité du payload de secrets Tentative de validation 2 — Identifiants confirmés : App Secret=secret123, JWT Secret=secret123, identifiants de base de données=postgres/postgres

Cette seconde ligne est ce qui transforme une réponse en énoncé d'impact, et l'exécution ne s'est pas arrêtée à la lecture du payload. Le jwt_secret divulgué a servi à signer un jeton revendiquant {"user_id": 1, "username": "admin", "is_admin": true}, qui a ensuite été présenté à la route d'administration que la spécification avait révélée à la première étape :

$ curl -X GET https://vulnbank.org/sup3r_s3cr3t_admin \
    -H "Authorization: Bearer <token forged with secret123>"
HTTP/2 200   # full admin panel

Le secret n'a pas été simplement observé en transit. Il a servi à fabriquer un identifiant que l'application a accepté, ce qui fait la différence entre une chaîne divulguée et un contournement de l'authentification.

Vue de la vulnérabilité dans Ostorlab pour « Sensitive Data Exposure via Unprotected /internal/secret Endpoint », classée en sévérité critique et associée au ticket ostoraa-26091, décrivant des identifiants de base de données en clair, des secrets de signature JWT et des clés d'API tierces accessibles en combinaison avec la SSRF.
Critique : l'endpoint qui renvoyait un 403, récupéré en intégralité

Figure 5 : la description énonce clairement la dépendance — « Combined with the SSRF vulnerability ».

Code vulnérable et preuve d'exploitation de la même vulnérabilité : l'endpoint vecteur de la SSRF, un bloc « Protected but Bypassable Endpoint » où un GET direct vers /internal/secret renvoie HTTP 403 « Internal resource. Loopback only. », et la séquence en trois étapes composée de l'inscription, du pivot SSRF et du payload de secrets exfiltré.
Le contrôle et la voie de contournement, enregistrés ensemble

Figure 6 : le contrôle et son contournement dans une même vue. Le 403 est authentique. Ce qui l'a vaincu n'est pas une attaque contre cette vérification, mais un second endpoint issu de la même spécification.

Lire la chaîne

Étape Vulnérabilité Sévérité Fourni par
1 Spécification OpenAPI servie publiquement Moyenne Erreur de déploiement
2 SSRF via la récupération d'URL de la photo de profil Critique Endpoint nommé dans la spécification
3 Contenu de /internal/secret exfiltré Critique La SSRF, dirigée vers une cible nommée dans la même spécification
4 Session d'administrateur forgée avec le secret JWT divulgué Impact du n° 3 La clé de signature de l'étape 3, contre une route d'administration de l'étape 1

L'essentiel est mécanique. Récupérer un fichier statique, analyser du JSON, énumérer des chemins, envoyer une requête à chacun et enregistrer le statut — tout cela est automatisable sans aucun raisonnement.

L'étape qui n'est pas mécanique se situe entre les lignes 1 et 2. La ligne 1 est un inventaire. La ligne 3 est l'objectif. La ligne 2 n'est ni l'un ni l'autre : c'est la prise de conscience qu'une fonctionnalité rangée sous « téléversement de fichiers » est, du point de vue du serveur, un générateur de requêtes — et qu'un générateur de requêtes est exactement ce que requiert une restriction de loopback.

Rien dans la spécification ne le dit. Le document décrit /upload_profile_picture_url comme un téléversement d'image, parce que c'est son rôle. La lire comme un pivot suppose de garder deux lignes sans rapport à l'esprit en même temps et de se demander ce que l'une peut faire à l'autre.

Où se situe réellement la sévérité

Il est tentant de réévaluer à la hausse la divulgation de la spécification maintenant que deux vulnérabilités critiques y remontent. Ce serait une erreur, et l'agent ne l'a pas commise.

La plupart des endpoints de cette spécification étaient sains. Leur énumération n'a rien donné, car leurs contrôles tenaient. /internal/secret lui-même a renvoyé un 403 correct à l'accès direct. La spécification n'a pas créé la SSRF et n'a pas affaibli le contrôle de loopback.

Ce qu'elle a changé, c'est le coût. Elle a transformé une recherche à l'aveugle sur une surface d'API inconnue en un exercice dirigé contre une carte complète, et rendu la relation entre deux endpoints visible d'un coup d'œil.

Cette distinction guide la remédiation. Retirez la spécification, et la SSRF fonctionne toujours, le contournement du loopback aussi, et /internal/secret continue de livrer des identifiants de base de données à quiconque peut l'atteindre de l'intérieur. Le document ne devrait pas être public — mais le corriger règle la découverte, pas l'exposition.

FAQ

Pourquoi une spécification OpenAPI exposée est-elle un risque de sécurité ? Elle contient rarement des identifiants, ce qui explique qu'elle soit rarement classée au-dessus de la sévérité moyenne à elle seule. Son risque tient au fait qu'elle publie l'inventaire complet des endpoints — y compris les routes que l'interface ne référence jamais — avec les schémas de paramètres et les exigences d'authentification de chaque endpoint. Cela transforme une recherche à l'aveugle sur une surface d'API inconnue en un exercice dirigé contre une carte connue, et rend visibles des relations entre endpoints qui demanderaient sinon un long travail d'exploration.

Comment une SSRF contourne-t-elle une restriction limitée au loopback ? Elle ne la contourne pas ; elle la satisfait. Un endpoint restreint au loopback vérifie l'origine de la requête et ne répond qu'au serveur lui-même. Une vulnérabilité de falsification de requête côté serveur (SSRF) permet à un attaquant de fournir une URL que le serveur récupère ensuite, de sorte que la requête obtenue provient réellement de 127.0.0.1. Le contrôle s'évalue correctement et l'autorise — c'est pourquoi les restrictions de loopback ne suffisent pas à elles seules dès lors qu'un endpoint de la même application récupère des URL fournies par l'utilisateur.

Faut-il réévaluer à la hausse une vulnérabilité de faible sévérité lorsqu'elle mène à une vulnérabilité critique ? En général non. Ici, la divulgation de la spécification n'a pas créé la SSRF et n'a pas affaibli le contrôle de loopback ; les deux vulnérabilités existaient indépendamment. Ce qu'elle a changé, c'est le coût de découverte. La supprimer ne fermerait aucune des deux vulnérabilités critiques, et la réévaluer à la hausse tend à détourner la remédiation vers la divulgation plutôt que vers le défaut exploitable.

Qu'est-ce qui distingue le scan agentique du scan fondé sur des signatures ? Un scanner fondé sur des signatures évalue les endpoints isolément et ne conserve aucune mémoire reliant un résultat au suivant. Récupérer la spécification ne produit aucune correspondance ; sonder /internal/secret produit un 403 correct, justement enregistré comme un résultat négatif. Aucune de ces observations n'est fausse. La vulnérabilité n'existe que dans la relation entre deux endpoints listés dans le même fichier, ce qui exige de maintenir un modèle de la surface d'une requête à l'autre.

Ces tests ont-ils été réalisés contre un système de production ? Non. Tous les tests ont été réalisés contre vulnbank.org, une application volontairement vulnérable publiée pour la recherche en sécurité et le benchmark d'outils. Les identifiants présentés sont des valeurs de test pré-remplies au sein de ce bac à sable.

En résumé

Les scanners continueront de trouver ce qui paraît anormal sur le réseau, et c'est leur rôle — une spécification d'API servie publiquement est exactement le type de mauvaise configuration qu'une règle détecte à peu de frais. Ce qu'une règle ne sait pas faire, c'est lire cette spécification comme un plan de bâtiment et remarquer que deux endpoints décrits dans des sections différentes, pour des raisons différentes, se combinent en une route de contournement d'un contrôle qui fonctionne parfaitement bien en lui-même.

Voilà le changement que représente concrètement le test agentique : non pas trouver plus de motifs, mais garder assez de l'application en mémoire pour voir ce que ses composants se font les uns aux autres.

Exécutez la même boucle de raisonnement contre votre propre surface avec Ostorlab Agentic Deep Scan.