Finding sur une plateforme EDI (client/éditeur anonymisés). Découvert par 34zY / Hamza Terir. Divulgation tentée, restée sans réponse. Domaines et nom produit retirés.
Contexte
Plateforme EDI multi-utilisateurs. Un champ de profil (« bio » / « à propos ») est réaffiché aux autres utilisateurs qui consultent le profil.
Comment je l’ai trouvée
Le champ de profil accepte du HTML arbitraire, sans sanitisation ni CSP. Le payload est stocké en base et réexécuté automatiquement dans le navigateur de chaque utilisateur qui affiche le profil :
'></script><script>/* PoC : lecture cookie de session */</script>
<img src=x onerror="/* exfiltration vers serveur attaquant */"><!--
Contrairement à un XSS reflété (qui exige un clic sur un lien piégé), ici la victime est touchée simplement en consultant la page.
1: Injection du payload dans le champ de profil.
2: Payload persisté en base après sauvegarde.
3: Consultation du profil, le HTML injecté s’exécute (defacement).
4: Le script s’exécute dans la session de la victime (lecture du cookie).
Validation technique sécurisée
Le retest utilise un document inerte et un marqueur sans script :
curl --fail-with-body --max-time 10 \
-H 'Authorization: Bearer TEST_TOKEN' \
-F 'file=@fixtures/benign-document.txt;type=text/plain' \
'https://AUTHORIZED_HOST/api/import'
Le rendu doit encoder toute valeur issue du document avant insertion dans HTML :
import html
def safe_text(value: str) -> str:
return html.escape(value, quote=True)
Les preuves de retest couvrent l’import, la consultation par un autre rôle et les en-têtes de réponse; aucun contenu actif n’est publié dans cet article.
La faille
XSS stocké (CWE-79) : absence d’encodage en sortie et de validation en entrée sur un champ persistant, sans CSP. Le formulaire d’édition n’a pas de token CSRF (CWE-352).
Impact
- Vol de session automatique pour tout utilisateur consultant le profil contaminé.
- Ciblage des administrateurs (une équipe support qui investigue un signalement) → escalade de privilèges.
- Propagation façon ver : un compte compromis peut contaminer d’autres profils.
- Impact de masse, persistant en base jusqu’au nettoyage.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:H = 9.3 (Critical).
Remédiation
- Encodage HTML en sortie de tous les champs de profil ; validation stricte en entrée.
- Bibliothèque de sanitisation (OWASP Java Encoder, DOMPurify côté rendu).
- Content-Security-Policy stricte ; cookies
HttpOnly. - Token CSRF sur l’édition de profil ; nettoyage des enregistrements déjà contaminés.
CWE-79 (Stored XSS) · CWE-352 (CSRF).