hacktricks/pentesting-web/oauth-to-account-takeover.md

28 KiB

OAuth vers la prise de contrôle de compte

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

Informations de base

Il existe plusieurs versions d'OAuth, vous pouvez lire https://oauth.net/2/ pour obtenir une compréhension de base.

Dans cet article, nous nous concentrerons sur le flux le plus courant que vous rencontrerez aujourd'hui, qui est le type d'autorisation de code d'autorisation OAuth 2.0. En essence, OAuth fournit aux développeurs un mécanisme d'autorisation permettant à une application d'accéder à des données ou d'effectuer certaines actions contre votre compte, depuis une autre application (le serveur d'autorisation).

Par exemple, disons que le site https://yourtweetreader.com a une fonctionnalité pour afficher tous les tweets que vous avez jamais envoyés, y compris les tweets privés. Pour ce faire, OAuth 2.0 est introduit. https://yourtweetreader.com vous demandera d'autoriser leur application Twitter à accéder à tous vos Tweets. Une page de consentement apparaîtra sur https://twitter.com affichant quelles permissions sont demandées, et qui est le développeur qui les demande. Une fois que vous autorisez la demande, https://yourtweetreader.com sera capable d'accéder à vos Tweets en votre nom.

Éléments importants à comprendre dans un contexte OAuth 2.0 :

  • propriétaire de la ressource : Le propriétaire de la ressource est l'utilisateur/entité accordant l'accès à leur ressource protégée, comme leurs Tweets de compte Twitter. Dans cet exemple, ce serait vous.
  • serveur de ressources : Le serveur de ressources est le serveur gérant les requêtes authentifiées après que l'application a obtenu un jeton d'accès au nom du propriétaire de la ressource. Dans cet exemple, ce serait https://twitter.com
  • application cliente : L'application cliente est l'application demandant l'autorisation du propriétaire de la ressource. Dans cet exemple, ce serait https://yourtweetreader.com.
  • serveur d'autorisation : Le serveur d'autorisation est le serveur émettant des jetons d'accès à l'application cliente après avoir authentifié avec succès le propriétaire de la ressource et obtenu l'autorisation. Dans l'exemple ci-dessus, ce serait https://twitter.com
  • client_id : Le client_id est l'identifiant de l'application. C'est un identifiant unique public, non secret.
  • client_secret : Le client_secret est un secret connu uniquement de l'application et du serveur d'autorisation. Il est utilisé pour générer des jetons d'accès
  • response_type : Le response_type est une valeur détaillant quel type de jeton est demandé, tel que code
  • scope : Le scope est le niveau d'accès demandé que l'application cliente demande au propriétaire de la ressource
  • redirect_uri : Le redirect_uri est l'URL vers laquelle l'utilisateur est redirigé après que l'autorisation est complète. Cela doit généralement correspondre à l'URL de redirection que vous avez préalablement enregistrée avec le service
  • state : Le paramètre state peut conserver des données entre le moment où l'utilisateur est dirigé vers le serveur d'autorisation et son retour. Il est important que cela soit une valeur unique car il sert de mécanisme de protection contre les CSRF s'il contient une valeur unique ou aléatoire par requête
  • grant_type : Le paramètre grant_type explique quel est le type de subvention, et quel jeton va être retourné
  • code : Ce code est le code d'autorisation reçu du serveur d'autorisation qui sera dans le paramètre de chaîne de requête “code” dans cette requête. Ce code est utilisé conjointement avec le client_id et le client_secret par l'application cliente pour récupérer un jeton d'accès
  • access_token : Le access_token est le jeton que l'application cliente utilise pour effectuer des requêtes API au nom d'un propriétaire de la ressource
  • refresh_token : Le refresh_token permet à une application d'obtenir un nouveau jeton d'accès sans solliciter l'utilisateur

Exemple réel

En rassemblant tout cela, voici à quoi ressemble un flux OAuth réel :

  1. Vous visitez https://yourtweetreader.com et cliquez sur le bouton “Intégrer avec Twitter”.
  2. https://yourtweetreader.com envoie une demande à https://twitter.com vous demandant, en tant que propriétaire de la ressource, d'autoriser l'application Twitter de https://yourtweetreader.com à accéder à vos Tweets. La demande ressemblera à :
https://twitter.com/auth
?response_type=code
&client_id=yourtweetreader_clientId
&redirect_uri=https%3A%2F%2Fyourtweetreader.com%2Fcallback
&scope=readTweets
&state=kasodk9d1jd992k9klaskdh123

3. Une page de consentement s'affichera :

4. Une fois accepté, Twitter enverra une requête au redirect_uri avec les paramètres code et state :

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123

5. https://yourtweetreader.com va ensuite prendre ce code, et en utilisant l'client_id et l'client_secret de leur application, va faire une demande depuis le serveur pour récupérer un access_token en votre nom, ce qui leur permettra d'accéder aux permissions auxquelles vous avez consenti :

POST /oauth/access_token
Host: twitter.com
...{"client_id": "yourtweetreader_clientId", "client_secret": "yourtweetreader_clientSecret", "code": "asd91j3jd91j92j1j9d1", "grant_type": "authorization_code"}

6. Finalement, le flux est complet et https://yourtweetreader.com effectuera un appel API à Twitter avec votre access_token pour accéder à vos Tweets.

Découvertes de Bug Bounty

Maintenant, la partie intéressante ! Il y a beaucoup de choses qui peuvent mal tourner dans une implémentation OAuth, voici les différentes catégories de bugs que je vois fréquemment :

Configuration faible de redirect_uri

Le redirect_uri est très important car des données sensibles, telles que le code, sont ajoutées à cette URL après autorisation. Si le redirect_uri peut être redirigé vers un serveur contrôlé par un attaquant, cela signifie que l'attaquant peut potentiellement prendre le contrôle du compte d'une victime en utilisant le code lui-même, et en accédant aux données de la victime.

La manière dont cela va être exploité variera selon le serveur d'autorisation. Certains vont uniquement accepter le chemin redirect_uri exact spécifié dans l'application cliente, mais certains vont accepter n'importe quoi dans le même domaine ou sous-répertoire du redirect_uri.

Selon la logique gérée par le serveur, il existe un certain nombre de techniques pour contourner un redirect_uri. Dans une situation où un redirect_uri est https://yourtweetreader.com/callback, celles-ci incluent :

  • Redirections ouvertes : https://yourtweetreader.com/callback?redirectUrl=https://evil.com
  • Traversée de chemin : https://yourtweetreader.com/callback/../redirect?url=https://evil.com
  • Regexes faibles de redirect_uri : https://yourtweetreader.com.evil.com
  • Injection HTML et vol de tokens via l'en-tête referer : https://yourtweetreader.com/callback/home/attackerimg.jpg

D'autres paramètres qui peuvent être vulnérables aux Redirections Ouvertes sont :

  • client_uri - URL de la page d'accueil de l'application cliente
  • policy_uri - URL que l'application cliente Partie Fiable fournit pour que l'utilisateur final puisse lire comment ses données de profil seront utilisées.
  • tos_uri - URL que l'application cliente Partie Fiable fournit pour que l'utilisateur final puisse lire les conditions de service de la Partie Fiable.
  • initiate_login_uri - URI utilisant le schéma https qui peut être utilisé par un tiers pour initier une connexion par le RP. Devrait également être utilisé pour la redirection côté client.

Tous ces paramètres sont optionnels selon les spécifications OAuth et OpenID et ne sont pas toujours pris en charge sur un serveur particulier, il est donc toujours utile d'identifier quels paramètres sont pris en charge sur votre serveur.

Si vous ciblez un serveur OpenID, le point de découverte à **.well-known/openid-configuration** contient parfois des paramètres tels que "registration_endpoint", "request_uri_parameter_supported", et "require_request_uri_registration". Ceux-ci peuvent vous aider à trouver le point de terminaison d'inscription et d'autres valeurs de configuration du serveur.

XSS dans l'implémentation de redirection

Comme mentionné dans ce rapport de bug bounty https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html il se peut que l'URL de redirection soit reflétée dans la réponse du serveur après l'authentification de l'utilisateur, étant vulnérable au XSS. Payload possible à tester :

https://app.victim.com/login?redirectUrl=https://app.victim.com/dashboard</script><h1>test</h1>

CSRF - Gestion incorrecte du paramètre state

Très souvent, le paramètre state est complètement omis ou utilisé de manière incorrecte. Si un paramètre state est inexistant, ou une valeur statique qui ne change jamais, le flux OAuth sera très probablement vulnérable au CSRF. Parfois, même s'il existe un paramètre state, l'application peut ne faire aucune validation de ce paramètre et une attaque fonctionnera. La manière d'exploiter cela serait de passer par le processus d'autorisation sur votre propre compte, et de mettre en pause juste après avoir autorisé. Vous rencontrerez alors une requête telle que :

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1

Après avoir reçu cette requête, vous pouvez alors abandonner la requête car ces codes sont généralement à usage unique. Vous pouvez ensuite envoyer cette URL à un utilisateur connecté, et cela ajoutera votre compte à leur compte. Au premier abord, cela peut ne pas sembler très sensible puisque vous ajoutez simplement votre compte à celui d'une victime. Cependant, de nombreuses implémentations OAuth sont utilisées à des fins de connexion, donc si vous pouvez ajouter votre compte Google qui est utilisé pour vous connecter, vous pourriez potentiellement réaliser une prise de contrôle de compte en un seul clic, car se connecter avec votre compte Google vous donnerait accès au compte de la victime.

Vous pouvez trouver un exemple à ce sujet dans ce compte-rendu de CTF et dans la machine HTB appelée Oouch.

J'ai également vu le paramètre state utilisé comme valeur de redirection supplémentaire à plusieurs reprises. L'application utilisera redirect_uri pour la redirection initiale, mais ensuite le paramètre state comme une seconde redirection qui pourrait contenir le code dans les paramètres de requête, ou l'en-tête referer.

Un point important à noter est que cela ne s'applique pas seulement aux situations de connexion et de prise de contrôle de compte. J'ai vu des mauvaises configurations dans :

  • Les intégrations Slack permettant à un attaquant d'ajouter leur compte Slack comme destinataire de toutes les notifications/messages
  • Les intégrations Stripe permettant à un attaquant de remplacer les informations de paiement et d'accepter les paiements des clients de la victime
  • Les intégrations PayPal permettant à un attaquant d'ajouter leur compte PayPal au compte de la victime, ce qui déposerait l'argent sur le PayPal de l'attaquant

Avant la prise de contrôle de compte

L'un des autres problèmes courants que je vois est lorsque les applications permettent de "Se connecter avec X" mais aussi avec un nom d'utilisateur/mot de passe. Il y a 2 façons différentes d'attaquer cela :

  1. Si l'application ne nécessite pas de vérification de l'email lors de la création du compte, essayez de créer un compte avec l'adresse email d'une victime et un mot de passe d'attaquant avant que la victime ne se soit enregistrée. Si la victime essaie ensuite de s'enregistrer ou de se connecter avec un tiers, comme Google, il est possible que l'application fasse une recherche, voie que l'email est déjà enregistré, puis lie leur compte Google au compte créé par l'attaquant. C'est une "prise de contrôle de compte préalable" où un attaquant aura accès au compte de la victime s'il l'a créé avant que la victime ne s'enregistre.
  2. Si une application OAuth ne nécessite pas de vérification de l'email, essayez de vous inscrire avec cette application OAuth puis changez l'adresse email pour une adresse email de la victime. Le même problème que ci-dessus pourrait exister, mais vous l'attaqueriez dans l'autre sens et obtiendriez l'accès au compte de la victime pour une prise de contrôle de compte.

Divulgation de secrets

Il est très important de reconnaître quels paramètres OAuth sont secrets, et de les protéger. Par exemple, divulguer le client_id est parfaitement acceptable et nécessaire, mais divulguer le client_secret est dangereux. Si cela est divulgué, l'attaquant peut potentiellement abuser de la confiance et de l'identité de l'application cliente de confiance pour voler les access_tokens des utilisateurs et les informations/accès privés pour leurs comptes intégrés. Pour revenir à notre exemple précédent, un problème que j'ai vu est d'effectuer cette étape depuis le client, au lieu du serveur :

5. https://yourtweetreader.com prendra ensuite ce code, et en utilisant l'client_id et l'client_secret de leur application, fera une requête depuis le serveur pour récupérer un access_token en votre nom, ce qui leur permettra d'accéder aux permissions auxquelles vous avez consenti.

Si cela est fait depuis le client, le client_secret sera divulgué et les utilisateurs pourront générer des access_tokens au nom de l'application. Avec un peu d'ingénierie sociale, ils peuvent également ajouter plus de scopes à l'autorisation OAuth et tout semblera légitime car la requête proviendra de l'application cliente de confiance.

Bruteforce du Client Secret

Vous pouvez essayer de bruteforcer le client_secret d'un fournisseur de services avec le fournisseur d'identité afin d'essayer de voler des comptes.
La requête de BF peut ressembler à :

POST /token HTTP/1.1
content-type: application/x-www-form-urlencoded
host: 10.10.10.10:3000
content-length: 135
Connection: close

code=77515&redirect_uri=http%3A%2F%2F10.10.10.10%3A3000%2Fcallback&grant_type=authorization_code&client_id=public_client_id&client_secret=[bruteforce]

Fuite du Header Referer avec Code + État

Une fois que le client a le code et l'état, s'ils sont reflétés dans le header Referer lorsqu'il navigue vers une autre page, alors il est vulnérable.

Jeton d'Accès Stocké dans l'Historique du Navigateur

Allez dans l'historique du navigateur et vérifiez si le jeton d'accès y est enregistré.

Code d'Autorisation Éternel

Le code d'autorisation devrait avoir une durée de vie limitée pour réduire la fenêtre de temps pendant laquelle un attaquant peut le voler et l'utiliser.

Jeton d'Autorisation/Actualisation non lié au client

Si vous pouvez obtenir le code d'autorisation et l'utiliser avec un client différent, alors vous pouvez prendre le contrôle d'autres comptes.

Chemins Heureux, XSS, Iframes & Messages Post pour fuite de code & valeurs d'état

AWS Cognito

Dans ce rapport de bug bounty : https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/ vous pouvez voir que le token que AWS Cognito renvoie à l'utilisateur peut avoir assez de permissions pour écraser les données de l'utilisateur. Par conséquent, si vous pouvez changer l'email de l'utilisateur pour un autre email d'utilisateur, vous pourriez être capable de prendre le contrôle d'autres comptes.

# Read info of the user
aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]

# Change email address
aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQ[...] --user-attributes Name=email,Value=imaginary@flickr.com
{
"CodeDeliveryDetailsList": [
{
"Destination": "i***@f***.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
}

Pour plus d'informations détaillées sur la manière d'abuser d'AWS Cognito, consultez :

{% embed url="https://cloud.hacktricks.xyz/pentesting-cloud/aws-pentesting/aws-unauthenticated-enum-access/aws-cognito-unauthenticated-enum" %}

Abuser des tokens d'autres applications

Comme mentionné dans ce compte-rendu, les flux OAuth qui s'attendent à recevoir le token (et non un code) pourraient être vulnérables s'ils ne vérifient pas que le token appartient à l'application.

Cela est dû au fait qu'un attaquant pourrait créer une application prenant en charge OAuth et se connecter avec Facebook (par exemple) dans sa propre application. Ensuite, une fois qu'une victime se connecte avec Facebook dans l'application de l'attaquant, l'attaquant pourrait obtenir le token OAuth de l'utilisateur donné à son application, et l'utiliser pour se connecter dans l'application OAuth de la victime en utilisant le token de l'utilisateur de la victime.

{% hint style="danger" %} Par conséquent, si l'attaquant parvient à faire accéder l'utilisateur à sa propre application OAuth, il pourra prendre le contrôle du compte de la victime dans les applications qui s'attendent à un token et qui ne vérifient pas si le token a été accordé à leur ID d'application. {% endhint %}

Selon ce compte-rendu, il était possible de faire ouvrir par une victime une page avec un returnUrl pointant vers l'hôte de l'attaquant. Cette information serait stockée dans un cookie (RU) et dans une étape ultérieure, l'invite demandera à l'utilisateur s'il souhaite donner accès à cet hôte de l'attaquant.

Pour contourner cette invite, il était possible d'ouvrir un onglet pour initier le flux Oauth qui définirait ce cookie RU en utilisant le returnUrl, de fermer l'onglet avant que l'invite ne soit affichée, et d'ouvrir un nouvel onglet sans cette valeur. Ensuite, l'invite n'informera pas sur l'hôte de l'attaquant, mais le cookie serait défini pour lui, donc le token sera envoyé à l'hôte de l'attaquant dans la redirection.

Paramètres SSRFs

L'une des URL cachées que vous pourriez manquer est le point de terminaison d'enregistrement de client dynamique. Afin d'authentifier les utilisateurs avec succès, les serveurs OAuth doivent connaître les détails de l'application cliente, tels que le "client_name", "client_secret", "redirect_uris", etc. Ces détails peuvent être fournis via une configuration locale, mais les serveurs d'autorisation OAuth peuvent également avoir un point de terminaison d'enregistrement spécial. Ce point de terminaison est normalement mappé sur "/register" et accepte les requêtes POST avec le format suivant :

POST /connect/register HTTP/1.1
Content-Type: application/json
Host: server.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJ ...

{
"application_type": "web",
"redirect_uris": ["https://client.example.org/callback"],
"client_name": "My Example",
"logo_uri": "https://client.example.org/logo.png",
"subject_type": "pairwise",
"sector_identifier_uri": "https://example.org/rdrct_uris.json",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client.example.org/public_keys.jwks",
"contacts": ["ve7jtb@example.org"],
"request_uris": ["https://client.example.org/rf.txt"]
}
Il existe deux spécifications qui définissent les paramètres dans cette requête : [RFC7591](https://tools.ietf.org/html/rfc7591) pour OAuth et [Openid Connect Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.3.1).

Comme vous pouvez le voir ici, un certain nombre de ces valeurs sont transmises via des références URL et semblent être des cibles potentielles pour [Server Side Request Forgery](https://portswigger.net/web-security/ssrf). En même temps, la plupart des serveurs que nous avons testés ne résolvent pas immédiatement ces URL lorsqu'ils reçoivent une demande d'inscription. Au lieu de cela, ils **enregistrent simplement ces paramètres et les utilisent plus tard pendant le flux d'autorisation OAuth**. En d'autres termes, c'est plus comme un SSRF de second ordre, ce qui rend la détection en boîte noire plus difficile.

Les paramètres suivants sont particulièrement intéressants pour les attaques SSRF :

* **logo\_uri** - URL qui fait référence à un **logo pour l'application cliente**. **Après avoir enregistré un client**, vous pouvez essayer d'appeler le point de terminaison d'autorisation OAuth ("/authorize") en utilisant votre nouveau "client\_id". Après la connexion, le serveur vous demandera d'approuver la demande et **peut afficher l'image à partir du "logo\_uri"**. Si le **serveur récupère l'image par lui-même**, le SSRF devrait être déclenché par cette étape. Alternativement, le serveur peut simplement inclure le logo via une balise **client-side "\<img>"**. Bien que cela ne conduise pas à SSRF, cela peut conduire à **XSS si l'URL n'est pas échappée**.
*   **jwks\_uri** - URL pour le document JSON Web Key Set \[JWK] du client. Ce jeu de clés est nécessaire sur le serveur pour valider les requêtes signées faites au point de terminaison du jeton lors de l'utilisation de JWT pour l'authentification du client \[RFC7523]. Pour tester le SSRF dans ce paramètre, **enregistrez une nouvelle application cliente avec un "jwks\_uri" malveillant**, effectuez le processus d'autorisation pour **obtenir un code d'autorisation pour n'importe quel utilisateur, puis récupérez le point de terminaison "/token"** avec le corps suivant :

`POST /oauth/token HTTP/1.1`\
`...`\

grant_type=authorization_code&code=n0esc3NRze7LTCu7iYzS6a5acc3f0ogp4&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJhbGci...

Si vulnérable, le serveur devrait effectuer une requête HTTP de serveur à serveur vers le "jwks_uri" fourni car il a besoin de cette clé pour vérifier la validité du paramètre "client_assertion" dans votre requête. Cela sera probablement seulement une vulnérabilité SSRF aveugle, car le serveur attend une réponse JSON appropriée.

  • sector_identifier_uri - Cette URL fait référence à un fichier avec un seul tableau JSON de valeurs redirect_uri. Si pris en charge, le serveur peut récupérer cette valeur dès que vous soumettez la demande d'inscription dynamique. Si cela n'est pas récupéré immédiatement, essayez d'effectuer une autorisation pour ce client sur le serveur. Comme il a besoin de connaître les redirect_uris pour compléter le flux d'autorisation, cela forcera le serveur à faire une requête à votre sector_identifier_uri malveillant.
  • request_uris - Un tableau des request_uris autorisés pour ce client. Le paramètre "request_uri" peut être pris en charge sur le point de terminaison d'autorisation pour fournir une URL qui contient un JWT avec les informations de la demande (voir https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.6.2).

Même si l'inscription dynamique du client n'est pas activée, ou si elle nécessite une authentification, nous pouvons essayer de réaliser un SSRF sur le point de terminaison d'autorisation simplement en utilisant "request_uri":\

GET /authorize?response_type=code%20id_token&client_id=sclient1&request_uri=https://ybd1rc7ylpbqzygoahtjh6v0frlh96.burpcollaborator.net/request.jwt

Note : ne confondez pas ce paramètre avec "redirect_uri". Le "redirect_uri" est utilisé pour la redirection après autorisation, tandis que "request_uri" est récupéré par le serveur au début du processus d'autorisation.

En même temps, de nombreux serveurs que nous avons vus n'autorisent pas des valeurs "request_uri" arbitraires : ils n'autorisent que des URL préalablement enregistrées pendant le processus d'inscription du client. C'est pourquoi nous devons fournir "request_uris": "https://ybd1rc7ylpbqzygoahtjh6v0frlh96.burpcollaborator.net/request.jwt" au préalable.

Conditions de course chez les fournisseurs OAuth

Si la plateforme que vous testez est un fournisseur OAuth lisez ceci pour tester d'éventuelles conditions de course.

Références

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres moyens de soutenir HackTricks :

```