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 :
- Reconnaissance — cartographier la surface : endpoints, paramètres, exigences d'authentification, artefacts exposés.
- Hypothèse — raisonner sur ce qui pourrait mal tourner au vu de ce qui a été observé.
- Test — envoyer la requête qui confirme ou infirme l'hypothèse.
- Validation — confirmer un impact réel plutôt qu'une réponse suggestive.
- 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.

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 |

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é.

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..."
}}
}}

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_urlvershttp://127.0.0.1:5000/internal/secreta 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.

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

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.