# CORS - Mauvaises configurations et contournement
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)! * Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family) * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
## Qu'est-ce que CORS ? La norme CORS (partage de ressources entre origines multiples) est nécessaire car elle **permet aux serveurs de spécifier qui peut accéder à ses ressources** et quelles **méthodes de requête HTTP sont autorisées** à partir de ressources externes. Une politique de **même origine** exige que le **serveur demandeur** d'une ressource et le serveur où se trouve la **ressource** utilisent le même protocole ([http://), nom de domaine](http://\), nom de domaine) et le même **port** (80). Ensuite, si le serveur force la politique de même origine, seules les pages web du même domaine et du même port pourront accéder aux ressources. Le tableau suivant montre comment la politique de même origine sera appliquée dans `http://normal-website.com/example/example.html` : | URL accédée | Accès autorisé ? | | ----------------------------------------- | ---------------------------------- | | `http://normal-website.com/example/` | Oui : même schéma, domaine et port | | `http://normal-website.com/example2/` | Oui : même schéma, domaine et port | | `https://normal-website.com/example/` | Non : schéma et port différents | | `http://en.normal-website.com/example/` | Non : domaine différent | | `http://www.normal-website.com/example/` | Non : domaine différent | | `http://normal-website.com:8080/example/` | Non : port différent\* | \*_Internet Explorer autorisera cet accès car IE ne prend pas en compte le numéro de port lors de l'application de la politique de même origine._ ### En-tête `Access-Control-Allow-Origin` La spécification de `Access-Control-Allow-Origin` permet **plusieurs origines**, ou la valeur **`null`**, ou le joker **`*`**. Cependant, **aucun navigateur ne prend en charge plusieurs origines** et il y a des **restrictions** sur l'utilisation du **joker** `*`. (_Le joker ne peut être utilisé seul, cela échouera `Access-Control-Allow-Origin: https://*.normal-website.com` et il ne peut pas être utilisé avec_ _Access-Control-Allow-Credentials: true_) Cet en-tête est **renvoyé par un serveur** lorsqu'un site Web demande une ressource inter-domaines, avec un en-tête `Origin` ajouté par le navigateur. ### En-tête `Access-Control-Allow-Credentials` Le comportement **par défaut** des demandes de ressources inter-domaines est que les **demandes** sont **transmises sans informations d'identification** telles que les cookies et l'en-tête d'autorisation. Cependant, le serveur inter-domaines peut **autoriser la lecture** de la **réponse** lorsque les **informations d'identification** sont **transmises** en définissant l'en-tête CORS **`Access-Control-Allow-Credentials`** sur **`true`**. Si la valeur est définie sur `true`, le navigateur enverra des informations d'identification (cookies, en-têtes d'autorisation ou certificats client TLS). ```javascript var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { console.log(xhr.responseText); } } xhr.open('GET', 'http://example.com/', true); xhr.withCredentials = true; xhr.send(null); ``` ```javascript fetch(url, { credentials: 'include' }) ``` ```javascript const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://bar.other/resources/post-here/'); xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); xhr.setRequestHeader('Content-Type', 'application/xml'); xhr.onreadystatechange = handler; xhr.send('Arun'); ``` ### Requête préalable Dans certaines circonstances, lorsqu'une requête entre domaines est : * inclut une **méthode HTTP non standard (HEAD, GET, POST)** * inclut de nouveaux **en-têtes** * inclut une valeur d'en-tête **Content-Type spéciale** {% hint style="info" %} **Vérifiez** [**dans ce lien**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple\_requests) **les conditions d'une requête pour éviter l'envoi d'une requête préalable** {% endhint %} la requête entre domaines est précédée d'une **requête** utilisant la méthode **`OPTIONS`**, et le protocole CORS nécessite une vérification initiale des **méthodes et des en-têtes autorisés avant de permettre la requête entre domaines**. Cela s'appelle la **vérification préalable**. Le serveur **renvoie une liste de méthodes autorisées** en plus de l'**origine de confiance** et le navigateur vérifie si la méthode demandée par le site Web est autorisée. {% hint style="danger" %} Notez que **même si une requête préalable n'est pas envoyée** parce que les conditions de la "requête régulière" sont respectées, la **réponse doit avoir les en-têtes d'autorisation** ou le **navigateur ne pourra pas lire la réponse** de la requête. {% endhint %} Par **exemple**, ceci est une requête préalable qui cherche à **utiliser la méthode `PUT`** avec un **en-tête de requête personnalisé** appelé `Special-Request-Header`: ``` OPTIONS /data HTTP/1.1 Host: ... Origin: https://normal-website.com Access-Control-Request-Method: PUT Access-Control-Request-Headers: Special-Request-Header ``` Le serveur pourrait renvoyer une réponse comme celle-ci : ``` HTTP/1.1 204 No Content ... Access-Control-Allow-Origin: https://normal-website.com Access-Control-Allow-Methods: PUT, POST, OPTIONS Access-Control-Allow-Headers: Special-Request-Header Access-Control-Allow-Credentials: true Access-Control-Max-Age: 240 ``` * `Access-Control-Allow-Headers` : Les en-têtes autorisés * `Access-Control-Expose-Headers` : Les en-têtes exposés * `Access-Control-Max-Age` : Définit une durée maximale pour mettre en cache la réponse préalable pour une réutilisation ultérieure * `Access-Control-Request-Headers` : L'en-tête que la requête cross-origin souhaite envoyer * `Access-Control-Request-Method` : La méthode que la requête cross-origin souhaite utiliser * `Origin` : Origine de la requête cross-origin (définie automatiquement par le navigateur) ![](../.gitbook/assets/preflight.svg) Notez que généralement (selon le type de contenu et les en-têtes définis), dans une requête **GET/POST, aucune requête préalable n'est envoyée** (la requête est envoyée **directement**), mais si vous voulez accéder aux **en-têtes/corps de la réponse**, elle doit contenir un en-tête _Access-Control-Allow-Origin_ qui l'autorise.\ **Par conséquent, CORS ne protège pas contre les attaques CSRF (mais peut être utile).** ### **Requête préalable pour les requêtes de réseau local** Lorsqu'une requête est envoyée à une adresse IP de réseau local, 2 en-têtes CORS supplémentaires sont envoyés : * L'en-tête de requête client `Access-Control-Request-Local-Network` indique que la requête est une requête de réseau local * L'en-tête de réponse serveur `Access-Control-Allow-Local-Network` indique qu'une ressource peut être partagée en toute sécurité avec des réseaux externes Une **réponse valide autorisant la requête de réseau local** doit également contenir dans la réponse l'en-tête `Access-Controls-Allow-Local_network: true` : ``` HTTP/1.1 200 OK ... Access-Control-Allow-Origin: https://public.example.com Access-Control-Allow-Methods: GET Access-Control-Allow-Credentials: true Access-Control-Allow-Local-Network: true Content-Length: 0 ... ``` {% hint style="warning" %} Notez que l'adresse IP linux **0.0.0.0** fonctionne pour **contourner** ces exigences pour accéder à localhost car cette adresse IP n'est pas considérée comme "locale". Il est également possible de **contourner les exigences du réseau local** si vous utilisez l'**adresse IP publique d'un point de terminaison local** (comme l'adresse IP publique du routeur). Car dans plusieurs cas, même si l'**adresse IP publique** est utilisée, si elle est **du réseau local**, l'accès sera autorisé. {% endhint %} ## Configurations mal configurées exploitables Notez que la plupart des **attaques réelles nécessitent que `Access-Control-Allow-Credentials`** soit défini sur **`true`** car cela permettra au navigateur d'envoyer les informations d'identification et de lire la réponse. Sans informations d'identification, de nombreuses attaques deviennent sans objet ; cela signifie que vous ne pouvez pas utiliser les cookies d'un utilisateur, il n'y a donc souvent rien à gagner en faisant en sorte que leur navigateur émette la demande plutôt que de l'émettre vous-même. Une exception notable est lorsque l'emplacement réseau de la **victime fonctionne comme une sorte d'authentification**. Vous pouvez utiliser le navigateur d'une victime comme proxy pour contourner l'authentification basée sur l'adresse IP et accéder aux applications intranet. En termes d'impact, cela est similaire au rebinding DNS, mais beaucoup moins compliqué à exploiter. ### Réflexion de l'`Origin` dans `Access-Control-Allow-Origin` Dans le monde réel, cela ne peut pas arriver car **ces 2 valeurs des en-têtes sont interdites ensemble**.\ Il est également vrai que de nombreux développeurs veulent **autoriser plusieurs URL dans le CORS**, mais les jokers de sous-domaine ou les listes d'URL ne sont pas autorisés. Ensuite, plusieurs développeurs **génèrent** l'en-tête \*\*`Access-Control-Allow-Origin`\*\* **dynamiquement**, et à plus d'une occasion, ils copient simplement la valeur de l'en-tête Origin. Dans ce cas, la **même vulnérabilité pourrait être exploitée.** Dans d'autres cas, le développeur pourrait vérifier que le **domaine** (_victimdomain.com_) **apparaît** dans l'en-tête **Origin**, puis, un attaquant peut utiliser un domaine appelé **`attackervictimdomain.com`** pour voler les informations confidentielles. ```html ``` ### L'origine `null` `null` est une valeur spéciale pour l'en-tête **Origin**. La spécification mentionne qu'elle est déclenchée par des redirections et des fichiers HTML locaux. Certaines applications peuvent autoriser l'origine `null` pour prendre en charge le développement local de l'application.\ C'est pratique car **plusieurs applications autoriseront cette valeur** dans le CORS et **n'importe quel site web peut facilement obtenir l'origine null en utilisant un iframe sandboxed** : ```html ``` ```html ``` ### **Bypasses Regexp** Si vous avez trouvé que le domaine _victim.com_ est **whitelisted**, vous devriez vérifier si _victim.com.**attacker.com**_ est également **whitelisted**, ou, dans le cas où vous pouvez **prendre le contrôle de certains sous-domaines**, vérifiez si _**somesubdomain**.victim.com_ est whitelisted. ### **Bypasses Regexp avancés** La plupart des regex utilisées pour identifier le domaine à l'intérieur de la chaîne se concentreront sur les caractères alphanumériques ASCII et `.-`. Ensuite, quelque chose comme `victimdomain.com{.attacker.com` à l'intérieur de l'en-tête Origin sera interprété par la regexp comme si le domaine était `victimdomain.com`, mais le navigateur (dans ce cas Safari prend en charge ce caractère dans le domaine) accédera au domaine `attacker.com`. Le caractère `_` (dans les sous-domaines) est non seulement pris en charge dans Safari, mais également dans Chrome et Firefox! **Ensuite, en utilisant l'un de ces sous-domaines, vous pourriez contourner certaines regex "communes" pour trouver le domaine principal d'une URL.** **Pour plus d'informations et de paramètres sur ce contournement, consultez:** [**https://www.corben.io/advanced-cors-techniques/**](https://www.corben.io/advanced-cors-techniques/) **et** [**https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397**](https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397) ![](<../.gitbook/assets/image (153).png>) ### Depuis XSS à l'intérieur d'un sous-domaine Un mécanisme de défense que les développeurs utilisent contre l'exploitation de CORS est de mettre en liste blanche les domaines qui demandent fréquemment l'accès aux informations. Cependant, ce n'est pas entièrement sécurisé, car si même **un** des sous-domaines du domaine **whitelisted** est **vulnérable** à d'autres exploits tels que **XSS**, cela peut permettre l'exploitation de CORS. Prenons un exemple, le code suivant montre la configuration qui permet aux sous-domaines de _requester.com_ d'accéder aux ressources de _provider.com_. ```javascript if ($_SERVER['HTTP_HOST'] == '*.requester.com') { //Access data else{ // unauthorized access} } ``` En supposant qu'un utilisateur ait accès à sub.requester.com mais pas à requester.com, et en supposant que `sub.requester.com` est vulnérable à XSS. L'utilisateur peut exploiter `provider.com` en utilisant la méthode d'attaque par script intersite. ### **Empoisonnement du cache côté serveur** Si les étoiles sont alignées, nous pourrions utiliser l'empoisonnement du cache côté serveur via l'injection d'en-tête HTTP pour créer une vulnérabilité [XSS stockée](https://portswigger.net/web-security/cross-site-scripting/stored). Si une application **reflète** l'en-tête **Origin** sans même le vérifier pour des caractères illégaux comme `,` nous avons effectivement une vulnérabilité d'injection d'en-tête HTTP contre les utilisateurs IE/Edge car Internet Explorer et Edge considèrent \r (0x0d) comme un terminateur d'en-tête HTTP valide : `GET / HTTP/1.1`\ `Origin: z[0x0d]Content-Type: text/html; charset=UTF-7` Internet Explorer voit la réponse comme : `HTTP/1.1 200 OK`\ `Access-Control-Allow-Origin: z`\ `Content-Type: text/html; charset=UTF-7` Ce n'est pas directement exploitable car il n'y a aucun moyen pour un attaquant de faire envoyer un en-tête malformé à un navigateur web, mais je peux **manuellement créer cette requête dans Burp Suite et un cache côté serveur peut enregistrer la réponse et la servir à d'autres personnes**. La charge utile que j'ai utilisée changera l'ensemble de caractères de la page en **UTF-7**, ce qui est notoirement utile pour créer des vulnérabilités XSS. ### **Empoisonnement du cache côté client** Vous avez peut-être déjà rencontré une page avec un [XSS réfléchi](https://portswigger.net/web-security/cross-site-scripting/reflected) dans un en-tête HTTP personnalisé. Disons qu'une page web reflète le contenu d'un en-tête personnalisé sans l'encoder : ```http GET / HTTP/1.1 Host: example.com X-User-id: <svg/onload=alert\(1\)> HTTP/1.1 200 OK Access-Control-Allow-Origin: \* Access-Control-Allow-Headers: X-User-id Content-Type: text/html ... Invalid user: <svg/onload=alert\(1\)>\ ``` Avec CORS, nous pouvons envoyer n'importe quelle valeur dans l'en-tête. En soi, cela est inutile car la réponse contenant notre JavaScript injecté ne sera pas rendue. Cependant, si "Vary: Origin" n'a pas été spécifié, la réponse peut être stockée dans le cache du navigateur et affichée directement lorsque le navigateur accède à l'URL associée. J'ai créé un fiddle pour [tenter cette attaque sur une URL de votre choix](https://jsfiddle.net/3gk8u8wu/3/). Étant donné que cette attaque utilise la mise en cache côté client, elle est en fait assez fiable. ```markup ``` ## Contournement ### XSSI (Inclusion de script entre sites) / JSONP XSSI désigne une sorte de vulnérabilité qui exploite le fait que, lorsqu'une ressource est incluse à l'aide de la balise `script`, la SOP ne s'applique pas, car les scripts doivent pouvoir être inclus entre domaines. Un attaquant peut ainsi lire tout ce qui a été inclus à l'aide de la balise `script`. Cela est particulièrement intéressant en ce qui concerne le JavaScript dynamique ou JSONP, lorsque des informations d'autorité ambiante telles que les cookies sont utilisées pour l'authentification. Les cookies sont inclus lors de la demande d'une ressource à partir d'un hôte différent. Plugin BurpSuite : [https://github.com/kapytein/jsonp](https://github.com/kapytein/jsonp) [**En savoir plus sur les différents types de XSSI et comment les exploiter ici.**](xssi-cross-site-script-inclusion.md) Essayez d'ajouter un **paramètre de rappel** (**`callback`**) dans la requête. Peut-être que la page était préparée pour envoyer les données sous forme de JSONP. Dans ce cas, la page renverra les données avec `Content-Type: application/javascript`, ce qui contournera la politique CORS. ![](<../.gitbook/assets/image (229).png>) ### Contournement facile (inutile ?) Vous pouvez demander à une application web de faire une demande pour vous et de renvoyer la réponse. Cela contournera le **`Access-Control-Allow-Origin`**, mais notez que les **informations d'identification de la victime finale ne seront pas envoyées** car vous **contacterez un domaine différent** (celui qui fera la demande pour vous). [**CORS-escape**](https://github.com/shalvah/cors-escape) CORS-escape fournit un **proxy** qui **transmet** notre **requête** ainsi que ses **en-têtes**, et il **falsifie** également l'en-tête **Origin** (Origin = **domaine demandé**). Ainsi, la **politique CORS est contournée**.\ Le code source est [sur Github](https://github.com/shalvah/cors-escape), vous pouvez donc **héberger le vôtre**. ```javascript xhr.open("GET", "https://cors-escape.herokuapp.com/https://maximum.blog/@shalvah/posts"); ``` [**simple-cors-escape**](https://github.com/shalvah/simple-cors-escape) La mise en place d'un proxy est un peu comme "transmettre" votre demande, exactement comme vous l'avez envoyée. Nous pourrions résoudre cela d'une manière alternative qui implique toujours que quelqu'un d'autre fasse la demande pour vous, mais cette fois, **au lieu de transmettre votre demande, le serveur fait sa propre demande, mais avec les paramètres que vous avez spécifiés.** ### Contournement Iframe + Popup Vous pouvez **contourner les vérifications CORS** telles que `e.origin === window.origin` en **créant un iframe** et **en ouvrant une nouvelle fenêtre** à partir de celui-ci. Plus d'informations sur la page suivante : {% content-ref url="xss-cross-site-scripting/iframes-in-xss-and-csp.md" %} [iframes-in-xss-and-csp.md](xss-cross-site-scripting/iframes-in-xss-and-csp.md) {% endcontent-ref %} ### DNS Rebinding via TTL ![](<../.gitbook/assets/image (108).png>) Essentiellement, vous faites **accéder la victime à votre page**, puis vous changez le **DNS de votre domaine (l'IP)** et le faites **pointer** vers la **page web de votre victime**. Vous faites exécuter quelque chose à votre **victime** (**JS**) lorsque le **TTL est terminé**, de sorte qu'une nouvelle demande DNS sera effectuée et que vous pourrez alors recueillir les informations (comme vous maintenez toujours **l'utilisateur dans votre domaine**, il n'enverra **aucun cookie** au serveur de la victime, donc cette option **abuse des privilèges spéciaux de l'IP de la victime**). Même si vous définissez le **TTL très bas** (0 ou 1), les **navigateurs ont un cache** qui vous **empêchera** d'**abuser** de cela pendant plusieurs secondes/minutes. Ainsi, cette technique est utile pour **contourner les vérifications explicites** (la victime **effectue explicitement une demande DNS** pour vérifier l'IP du domaine et lorsque le bot est appelé, il fera le sien). Ou lorsque vous pouvez avoir un **utilisateur/bot sur la même page pendant une longue période** (vous pouvez donc **attendre** que le **cache expire**). Si vous avez besoin de quelque chose de rapide pour abuser de cela, vous pouvez utiliser un service comme [https://lock.cmpxchg8b.com/rebinder.html](https://lock.cmpxchg8b.com/rebinder.html). Si vous voulez exécuter votre propre serveur de rebinding DNS, vous pouvez utiliser quelque chose comme [**DNSrebinder**](https://github.com/mogwailabs/DNSrebinder)**,** puis **exposer** votre **port local 53/udp**, créer un **registre A pointant vers celui-ci** (ns.example.com), et créer un **registre NS** pointant vers le **sous-domaine A précédemment créé** (ns.example.com).\ Ensuite, tout sous-domaine de ce sous-domaine (ns.example.com) sera résolu par votre hôte. Consultez également le **serveur en cours d'exécution publiquement dans** [**http://rebind.it/singularity.html**](http://rebind.it/singularity.html) ### DNS Rebinding via **DNS Cache Flooding** Comme expliqué dans la section précédente, les **navigateurs** ont les adresses IP des domaines **mises en cache plus longtemps** que celle spécifiée dans le TTL. Cependant, il existe un moyen de contourner cette défense. Vous pouvez avoir un **service worker qui inondera le cache DNS pour forcer une deuxième demande DNS**. Ainsi, le flux sera comme suit : 1. La demande DNS a répondu avec l'adresse de l'attaquant 2. Le service worker inonde le cache DNS (le nom de serveur attaquant mis en cache est supprimé) 3. Deuxième demande DNS cette fois a répondu avec 127.0.0.1 ![](<../.gitbook/assets/image (375) (1).png>) _Le bleu est la première demande DNS et l'orange est l'inondation._ ### DNS Rebinding via **Cache** Comme expliqué dans la section précédente, les **navigateurs** ont les adresses IP des domaines **mises en cache plus longtemps** que celle spécifiée dans le TTL. Cependant, il existe un autre moyen de contourner cette défense. Vous pouvez **créer 2 enregistrements A** (ou **1 avec 2 IPs**, selon le fournisseur) pour le **même sous-domaine** dans le **fournisseur DNS** et lorsque le navigateur les vérifie, il les obtiendra tous les deux. Maintenant, si le **navigateur** décide d'**utiliser l'adresse IP de l'attaquant en premier**, l'**attaquant** pourra **servir la charge utile** qui **effectuera des demandes HTTP** vers le même **domaine**. Cependant, maintenant que l'attaquant connaît l'IP de la victime, **il cessera de répondre au navigateur de la victime**. Lorsque le navigateur constate que le **domaine ne lui répond pas**, il **utilisera la deuxième IP donnée**, donc il **accédera à un endroit différent en contournant SOP**. L'attaquant peut en profiter pour **obtenir les informations et les exfiltrer**. {% hint style="warning" %} Notez que pour accéder à localhost, vous devriez essayer de réassocier **127.0.0.1** dans Windows et **0.0.0.0** dans Linux.\ Des fournisseurs tels que Godaddy ou Cloudflare ne m'ont pas permis d'utiliser l'adresse IP 0.0.0.0, mais AWS route53 m'a permis de créer un enregistrement A avec 2 IPs dont l'une était "0.0.0.0" {% endhint %} ![](<../.gitbook/assets/image (620) (4).png>) Pour plus d'informations, vous pouvez consulter [https://unit42.paloaltonetworks.com/dns-rebinding/](https://unit42.paloaltonetworks.com/dns-rebinding/) ### Autres contournements courants * Si les **adresses IP internes ne sont pas autorisées**,