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

27 KiB

OAuth para Tomada de Conta

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Informações Básicas

Existem algumas versões diferentes de OAuth, você pode ler https://oauth.net/2/ para obter um entendimento básico.

Neste artigo, vamos nos concentrar no fluxo mais comum que você encontrará hoje, que é o tipo de concessão de código de autorização OAuth 2.0. Em essência, o OAuth fornece aos desenvolvedores um mecanismo de autorização para permitir que uma aplicação acesse dados ou execute certas ações contra sua conta, a partir de outra aplicação (o servidor de autorização).

Por exemplo, digamos que o site https://yourtweetreader.com tenha funcionalidade para exibir todos os tweets que você já enviou, incluindo tweets privados. Para fazer isso, o OAuth 2.0 é introduzido. https://yourtweetreader.com pedirá que você autorize sua aplicação do Twitter a acessar todos os seus Tweets. Uma página de consentimento aparecerá em https://twitter.com exibindo quais permissões estão sendo solicitadas, e quem é o desenvolvedor que as solicita. Uma vez que você autorize a solicitação, https://yourtweetreader.com será capaz de acessar seus Tweets em seu nome.

Elementos que são importantes entender em um contexto de OAuth 2.0:

  • proprietário do recurso: O proprietário do recurso é o usuário/entidade concedendo acesso ao seu recurso protegido, como seus Tweets da conta do Twitter. Neste exemplo, seria você.
  • servidor de recursos: O servidor de recursos é o servidor que lida com solicitações autenticadas após a aplicação ter obtido um token de acesso em nome do proprietário do recurso. Neste exemplo, seria https://twitter.com
  • aplicação cliente: A aplicação cliente é a aplicação solicitando autorização do proprietário do recurso. Neste exemplo, seria https://yourtweetreader.com.
  • servidor de autorização: O servidor de autorização é o servidor que emite tokens de acesso para a aplicação cliente após autenticar com sucesso o proprietário do recurso e obter autorização. No exemplo acima, seria https://twitter.com
  • client_id: O client_id é o identificador para a aplicação. Este é um identificador único, não secreto e público.
  • client_secret: O client_secret é um segredo conhecido apenas pela aplicação e pelo servidor de autorização. Isso é usado para gerar tokens de acesso
  • response_type: O response_type é um valor que detalha qual tipo de token está sendo solicitado, como code
  • scope: O scope é o nível de acesso solicitado que a aplicação cliente está pedindo do proprietário do recurso
  • redirect_uri: O redirect_uri é a URL para a qual o usuário é redirecionado após a autorização estar completa. Geralmente, deve corresponder à URL de redirecionamento que você registrou previamente com o serviço
  • state: O parâmetro state pode persistir dados entre o usuário ser direcionado ao servidor de autorização e voltar novamente. É importante que este seja um valor único, pois serve como um mecanismo de proteção contra CSRF se contiver um valor único ou aleatório por solicitação
  • grant_type: O parâmetro grant_type explica qual é o tipo de concessão, e qual token será retornado
  • code: Este code é o código de autorização recebido do servidor de autorização que estará no parâmetro de string de consulta “code” nesta solicitação. Este código é usado em conjunto com o client_id e client_secret pela aplicação cliente para buscar um token de acesso
  • access_token: O access_token é o token que a aplicação cliente usa para fazer solicitações de API em nome de um proprietário do recurso
  • refresh_token: O refresh_token permite que uma aplicação obtenha um novo token de acesso sem solicitar ao usuário

Exemplo Real

Juntando tudo isso, aqui está como um fluxo real de OAuth parece:

  1. Você visita https://yourtweetreader.com e clica no botão “Integrar com o Twitter”.
  2. https://yourtweetreader.com envia uma solicitação para https://twitter.com pedindo a você, o proprietário do recurso, para autorizar a aplicação do Twitter de https://yourtweetreader.com a acessar seus Tweets. A solicitação será assim:
https://twitter.com/auth
?response_type=code
&client_id=yourtweetreader_clientId
&redirect_uri=https%3A%2F%2Fyourtweetreader.com%2Fcallback
&scope=readTweets
&state=kasodk9d1jd992k9klaskdh123

3. Você será direcionado para uma página de consentimento:

4. Uma vez aceito, o Twitter enviará uma requisição de volta para o redirect_uri com os parâmetros code e state:

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

5. https://yourtweetreader.com então usará esse code, e utilizando o client_id e client_secret da aplicação, fará uma solicitação do servidor para recuperar um access_token em seu nome, o que permitirá a eles acessar as permissões às quais você consentiu:

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

6. Finalmente, o fluxo está completo e https://yourtweetreader.com fará uma chamada de API para o Twitter com seu access_token para acessar seus Tweets.

Descobertas de Bug Bounty

Agora, a parte interessante! Há muitas coisas que podem dar errado em uma implementação OAuth, aqui estão as diferentes categorias de bugs que eu vejo frequentemente:

Configuração fraca de redirect_uri

O redirect_uri é muito importante porque dados sensíveis, como o code, são anexados a esta URL após a autorização. Se o redirect_uri puder ser redirecionado para um servidor controlado pelo atacante, isso significa que o atacante pode potencialmente tomar controle da conta de uma vítima usando o code por conta própria e ganhando acesso aos dados da vítima.

A maneira como isso será explorado vai variar de acordo com o servidor de autorização. Alguns vão aceitar apenas o mesmo caminho exato de redirect_uri especificado na aplicação cliente, mas alguns vão aceitar qualquer coisa no mesmo domínio ou subdiretório do redirect_uri.

Dependendo da lógica tratada pelo servidor, há uma série de técnicas para contornar um redirect_uri. Em uma situação onde um redirect_uri é https://yourtweetreader.com/callback, estas incluem:

  • Redirecionamentos abertos: https://yourtweetreader.com/callback?redirectUrl=https://evil.com
  • Traversal de caminho: https://yourtweetreader.com/callback/../redirect?url=https://evil.com
  • Regexes fracos de redirect_uri: https://yourtweetreader.com.evil.com
  • Injeção de HTML e roubo de tokens via cabeçalho referer: https://yourtweetreader.com/callback/home/attackerimg.jpg

Outros parâmetros que podem ser vulneráveis a Redirecionamentos Abertos são:

  • client_uri - URL da página inicial da aplicação cliente
  • policy_uri - URL que a aplicação cliente Parte Confiante fornece para que o usuário final possa ler sobre como seus dados de perfil serão usados.
  • tos_uri - URL que a aplicação cliente Parte Confiante fornece para que o usuário final possa ler sobre os termos de serviço da Parte Confiante.
  • initiate_login_uri - URI usando o esquema https que um terceiro pode usar para iniciar um login pela RP. Também deve ser usado para redirecionamento do lado do cliente.

Todos esses parâmetros são opcionais de acordo com as especificações OAuth e OpenID e nem sempre são suportados em um servidor específico, então vale sempre a pena identificar quais parâmetros são suportados no seu servidor.

Se você mirar em um servidor OpenID, o ponto de descoberta em **.well-known/openid-configuration** às vezes contém parâmetros como "registration_endpoint", "request_uri_parameter_supported", e "require_request_uri_registration". Estes podem ajudá-lo a encontrar o ponto de registro e outros valores de configuração do servidor.

XSS na implementação de redirecionamento

Como mencionado neste relatório de bug bounty https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html pode ser possível que a URL de redirecionamento esteja sendo refletida na resposta do servidor após o usuário se autenticar, sendo vulnerável a XSS. Payload possível para teste:

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

CSRF - Tratamento inadequado do parâmetro state

Muito frequentemente, o parâmetro state é completamente omitido ou usado de maneira incorreta. Se um parâmetro state é inexistente, ou um valor estático que nunca muda, o fluxo OAuth provavelmente será vulnerável a CSRF. Às vezes, mesmo que exista um parâmetro state, a aplicação pode não fazer nenhuma validação do parâmetro e um ataque funcionará. A maneira de explorar isso seria passar pelo processo de autorização na sua própria conta e pausar logo após autorizar. Você então se deparará com uma solicitação como:

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1

Após receber essa solicitação, você pode descartar a solicitação, pois esses códigos são tipicamente de uso único. Em seguida, você pode enviar esta URL para um usuário logado, e ela adicionará sua conta à conta dele. A princípio, isso pode não parecer muito sensível, já que você está simplesmente adicionando sua conta à conta de uma vítima. No entanto, muitas implementações de OAuth são para fins de login, então, se você puder adicionar sua conta do Google, que é usada para fazer login, você poderia potencialmente realizar um Account Takeover com um único clique, pois fazer login com sua conta do Google daria acesso à conta da vítima.

Você pode encontrar um exemplo sobre isso neste CTF writeup e na HTB box chamada Oouch.

Também vi o parâmetro state sendo usado como um valor de redirecionamento adicional várias vezes. O aplicativo usará redirect_uri para o redirecionamento inicial, mas depois o parâmetro state como um segundo redirecionamento que poderia conter o code dentro dos parâmetros da query, ou no cabeçalho referer.

Uma coisa importante a notar é que isso não se aplica apenas a situações de login e account takeover. Vi configurações incorretas em:

  • Integrações do Slack permitindo que um atacante adicione sua conta do Slack como destinatário de todas as notificações/mensagens
  • Integrações do Stripe permitindo que um atacante substitua informações de pagamento e aceite pagamentos dos clientes da vítima
  • Integrações do PayPal permitindo que um atacante adicione sua conta do PayPal à conta da vítima, o que depositaria dinheiro na conta do PayPal do atacante

Pre Account Takeover

Um dos outros problemas mais comuns que vejo é quando aplicativos permitem "Sign in with X", mas também username/password. Existem 2 maneiras diferentes de atacar isso:

  1. Se o aplicativo não exigir verificação de e-mail na criação da conta, tente criar uma conta com o e-mail de uma vítima e senha do atacante antes de a vítima se registrar. Se a vítima então tentar se registrar ou fazer login com um terceiro, como o Google, é possível que o aplicativo faça uma busca, veja que o e-mail já está registrado e então vincule sua conta do Google à conta criada pelo atacante. Isso é um "pre account takeover" onde um atacante terá acesso à conta da vítima se a criou antes do registro da vítima.
  2. Se um aplicativo OAuth não exigir verificação de e-mail, tente se inscrever com esse aplicativo OAuth e depois mude o endereço de e-mail para o e-mail de uma vítima. O mesmo problema acima poderia existir, mas você estaria atacando de outra direção e obtendo acesso à conta da vítima para um account takeover.

Disclosure of Secrets

É muito importante reconhecer quais dos muitos parâmetros OAuth são secretos e protegê-los. Por exemplo, vazar o client_id é perfeitamente aceitável e necessário, mas vazar o client_secret é perigoso. Se isso for vazado, o atacante pode potencialmente abusar da confiança e identidade do aplicativo cliente confiável para roubar access_tokens do usuário e informações/acesso privado para suas contas integradas. Voltando ao nosso exemplo anterior, um problema que vi é realizar esta etapa do cliente, em vez do servidor:

5. https://yourtweetreader.com então pegará esse code, e usando o client_id e client_secret do aplicativo, fará uma solicitação do servidor para recuperar um access_token em seu nome, o que permitirá a eles acessar as permissões às quais você consentiu.

Se isso for feito do cliente, o client_secret será vazado e os usuários poderão gerar access_tokens em nome do aplicativo. Com um pouco de engenharia social, eles também podem adicionar mais escopos à autorização OAuth e tudo parecerá legítimo, pois a solicitação virá do aplicativo cliente confiável.

Client Secret Bruteforce

Você pode tentar bruteforce o client_secret de um provedor de serviço com o provedor de identidade para tentar roubar contas.
A solicitação para BF pode parecer semelhante a:

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]

Cabeçalho Referer vazando Código + Estado

Uma vez que o cliente tem o código e estado, se estes são refletidos dentro do cabeçalho Referer quando ele navega para uma página diferente, então está vulnerável.

Token de Acesso Armazenado no Histórico do Navegador

Vá para o histórico do navegador e verifique se o token de acesso está salvo lá.

Código de Autorização Permanente

O código de autorização deve viver apenas por algum tempo para limitar a janela de tempo onde um atacante pode roubar e usá-lo.

Token de Autorização/Atualização não vinculado ao cliente

Se você pode obter o código de autorização e usá-lo com um cliente diferente, então você pode assumir o controle de outras contas.

Caminhos Felizes, XSS, Iframes & Post Messages para vazar valores de código & estado

AWS Cognito

Neste relatório de bug bounty: https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/ você pode ver que o token que o AWS Cognito devolve ao usuário pode ter permissões suficientes para sobrescrever os dados do usuário. Portanto, se você pode mudar o email do usuário por um email de outro usuário, você pode ser capaz de assumir o controle de outras contas.

# 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"
}
]
}

Para mais informações detalhadas sobre como abusar do AWS cognito, confira:

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

Abusando de tokens de outros Apps

Como mencionado neste relatório, fluxos OAuth que esperam receber o token (e não um código) podem ser vulneráveis se não verificarem que o token pertence ao app.

Isso ocorre porque um atacante poderia criar uma aplicação suportando OAuth e fazer login com o Facebook (por exemplo) em sua própria aplicação. Então, uma vez que uma vítima faça login com o Facebook na aplicação do atacante, o atacante poderia obter o token OAuth do usuário dado à sua aplicação e usá-lo para fazer login na aplicação OAuth da vítima usando o token do usuário da vítima.

{% hint style="danger" %} Portanto, se o atacante conseguir fazer com que o usuário acesse sua própria aplicação OAuth, ele poderá assumir o controle da conta da vítima em aplicações que estão esperando um token e não estão verificando se o token foi concedido ao ID de seu app. {% endhint %}

De acordo com este relatório, era possível fazer com que uma vítima abrisse uma página com um returnUrl apontando para o host do atacante. Essa informação seria armazenada em um cookie (RU) e em uma etapa posterior o prompt irá perguntar ao usuário se ele quer dar acesso ao host do atacante.

Para contornar este prompt, era possível abrir uma aba para iniciar o fluxo Oauth que configuraria este cookie RU usando o returnUrl, fechar a aba antes que o prompt fosse exibido e abrir uma nova aba sem esse valor. Então, o prompt não informará sobre o host do atacante, mas o cookie estaria configurado para ele, então o token seria enviado para o host do atacante no redirecionamento.

Parâmetros SSRFs

Uma das URLs ocultas que você pode perder é o ponto de registro de cliente dinâmico. Para autenticar usuários com sucesso, os servidores OAuth precisam conhecer detalhes sobre a aplicação cliente, como "client_name", "client_secret", "redirect_uris", entre outros. Esses detalhes podem ser fornecidos via configuração local, mas os servidores de autorização OAuth também podem ter um ponto de registro especial. Este ponto normalmente é mapeado para "/register" e aceita solicitações POST com o seguinte formato:

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"]
}

Existem duas especificações que definem parâmetros nesta solicitação: RFC7591 para OAuth e Openid Connect Registration 1.0.

Como você pode ver aqui, vários desses valores são passados por referências de URL e parecem alvos potenciais para Server Side Request Forgery. Ao mesmo tempo, a maioria dos servidores que testamos não resolve essas URLs imediatamente ao receberem uma solicitação de registro. Em vez disso, eles apenas salvam esses parâmetros e os usam mais tarde durante o fluxo de autorização OAuth. Em outras palavras, isso é mais como um SSRF de segunda ordem, o que torna a detecção de caixa-preta mais difícil.

Os seguintes parâmetros são particularmente interessantes para ataques SSRF:

  • logo_uri - URL que referencia um logotipo para o aplicativo cliente. Após registrar um cliente, você pode tentar chamar o ponto de extremidade de autorização OAuth ("/authorize") usando seu novo "client_id". Após o login, o servidor pedirá para você aprovar a solicitação e pode exibir a imagem do "logo_uri". Se o servidor buscar a imagem por si mesmo, o SSRF deve ser acionado por esta etapa. Alternativamente, o servidor pode apenas incluir o logotipo por meio de uma tag "<img>" do lado do cliente. Embora isso não leve a SSRF, pode levar a XSS se a URL não for escapada.
  • jwks_uri - URL para o documento do conjunto de chaves da Web JSON [JWK] do cliente. Este conjunto de chaves é necessário no servidor para validar solicitações assinadas feitas ao ponto de extremidade do token ao usar JWTs para autenticação do cliente [RFC7523]. Para testar SSRF neste parâmetro, registre um novo aplicativo cliente com um "jwks_uri" malicioso, realize o processo de autorização para obter um código de autorização para qualquer usuário e, em seguida, busque o ponto de extremidade "/token" com o seguinte corpo:

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...

Se vulnerável, o servidor deve realizar uma solicitação HTTP de servidor para servidor para o "jwks_uri" fornecido porque precisa dessa chave para verificar a validade do parâmetro "client_assertion" em sua solicitação. Isso provavelmente será apenas uma vulnerabilidade SSRF cega, já que o servidor espera uma resposta JSON adequada.

  • sector_identifier_uri - Esta URL referencia um arquivo com um único array JSON de valores redirect_uri. Se suportado, o servidor pode buscar esse valor assim que você enviar a solicitação de registro dinâmico. Se isso não for buscado imediatamente, tente realizar a autorização para este cliente no servidor. Como ele precisa saber os redirect_uris para completar o fluxo de autorização, isso forçará o servidor a fazer uma solicitação ao seu sector_identifier_uri malicioso.
  • request_uris - Um array dos request_uris permitidos para este cliente. O parâmetro "request_uri" pode ser suportado no ponto de extremidade de autorização para fornecer uma URL que contém um JWT com as informações da solicitação (veja https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.6.2).

Mesmo que o registro de cliente dinâmico não esteja habilitado, ou exija autenticação, podemos tentar realizar SSRF no ponto de extremidade de autorização simplesmente usando "request_uri":\

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

Nota: não confunda este parâmetro com "redirect_uri". O "redirect_uri" é usado para redirecionamento após autorização, enquanto "request_uri" é buscado pelo servidor no início do processo de autorização.

Ao mesmo tempo, muitos servidores que vimos não permitem valores arbitrários de "request_uri": eles apenas permitem URLs na lista de permissões que foram pré-registrados durante o processo de registro do cliente. É por isso que precisamos fornecer "request_uris": "https://ybd1rc7ylpbqzygoahtjh6v0frlh96.burpcollaborator.net/request.jwt" antecipadamente.

Condições de Corrida em Provedores OAuth

Se a plataforma que você está testando é um provedor OAuth leia isto para testar possíveis Condições de Corrida.

Referências

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks: