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

26 KiB

OAuth para Tomada de Conta

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

Informações Básicas

Existem algumas versões diferentes do OAuth, você pode ler https://oauth.net/2/ para ter uma compreensão básica.

Neste artigo, estaremos nos concentrando 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 um aplicativo acesse dados ou execute determinadas ações em sua conta, a partir de outro aplicativo (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 seu aplicativo 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 está solicitando. Depois de autorizar a solicitação, https://yourtweetreader.com será capaz de acessar seus tweets em seu nome.

Elementos importantes para entender em um contexto OAuth 2.0:

  • dono do recurso: O dono do recurso é o usuário/entidade que concede 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 o aplicativo obter um token de acesso em nome do dono do recurso. Neste exemplo, seria https://twitter.com
  • aplicativo cliente: O aplicativo cliente é o aplicativo que solicita autorização do dono 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 o aplicativo cliente após autenticar com sucesso o dono do recurso e obter autorização. No exemplo acima, seria https://twitter.com
  • client_id: O client_id é o identificador do aplicativo. Este é um identificador único público, não secreto.
  • client_secret: O client_secret é um segredo conhecido apenas pelo aplicativo e pelo servidor de autorização. Isso é usado para gerar access_tokens
  • response_type: O response_type é um valor para detalhar qual tipo de token está sendo solicitado, como code
  • scope: O scope é o nível de acesso solicitado que o aplicativo cliente está solicitando do dono do recurso
  • redirect_uri: O redirect_uri é a URL para a qual o usuário é redirecionado após a conclusão da autorização. Isso geralmente deve corresponder à URL de redirecionamento que você registrou anteriormente no serviço
  • state: O parâmetro state pode persistir dados entre o usuário sendo direcionado para o servidor de autorização e de volta novamente. É importante que este seja um valor único, pois serve como um mecanismo de proteção 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 da string de consulta "code" nesta solicitação. Este código é usado em conjunto com o client_id e client_secret pelo aplicativo cliente para buscar um access_token
  • access_token: O access_token é o token que o aplicativo cliente usa para fazer solicitações de API em nome do dono do recurso
  • refresh_token: O refresh_token permite que um aplicativo obtenha um novo access_token sem solicitar ao usuário

Exemplo Real

Colocando tudo isso junto, aqui está o que um fluxo OAuth real 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 que você, o dono do recurso, autorize o aplicativo do Twitter do 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
  1. Você será solicitado com uma página de consentimento:

  1. Uma vez aceito, o Twitter enviará uma solicitação de volta para o redirect_uri com os parâmetros code e state:
https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1&state=kasodk9d1jd992k9klaskdh123

5. Em seguida, o https://yourtweetreader.com irá pegar esse code e, usando o client_id e client_secret da aplicação deles, fará uma solicitação ao servidor para recuperar um access_token em seu nome, o que permitirá que eles acessem as permissões que 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! Existem muitas coisas que podem dar errado em uma implementação OAuth, aqui estão as diferentes categorias de bugs que vejo com frequência:

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 assumir a conta de uma vítima usando o code eles mesmos 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 só vão aceitar o mesmo caminho 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 manipulada pelo servidor, existem várias técnicas para contornar um redirect_uri. Em uma situação em que um redirect_uri é https://yourtweetreader.com/callback, essas incluem:

  • Redirecionamentos abertos: https://yourtweetreader.com/callback?redirectUrl=https://evil.com
  • Traversal de caminho: https://yourtweetreader.com/callback/../redirect?url=https://evil.com
  • Regexes de redirect_uri fracos: 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 o aplicativo cliente do Relying Party fornece para que o usuário final possa ler sobre como seus dados de perfil serão usados.
  • tos_uri - URL que o cliente do Relying Party fornece para que o usuário final possa ler sobre os termos de serviço do Relying Party.
  • initiate_login_uri - URI usando o esquema https que um terceiro pode usar para iniciar um login pelo 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 do OAuth e OpenID e nem sempre são suportados em um servidor específico, portanto, sempre vale a pena identificar quais parâmetros são suportados em seu servidor.

Se você direcionar 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". Isso pode 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. Carga útil possível para teste:

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

CSRF - Manipulação inadequada do parâmetro de estado

Muitas vezes, o parâmetro state é completamente omitido ou usado de maneira incorreta. Se um parâmetro de estado não existir ou for um valor estático que nunca muda, o fluxo do OAuth provavelmente estará vulnerável a CSRF. Às vezes, mesmo que haja um parâmetro state, a aplicação pode não validar o parâmetro e um ataque funcionará. A maneira de explorar isso seria passar pelo processo de autorização em sua própria conta e pausar logo após a autorização. Você encontrará uma solicitação como esta:

https://yourtweetreader.com?code=asd91j3jd91j92j1j9d1

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

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

Também vi o parâmetro de estado usado como um valor de redirecionamento adicional várias vezes. O aplicativo usará redirect_uri para o redirecionamento inicial, mas então o parâmetro state como um segundo redirecionamento que poderia conter o code nos parâmetros de consulta ou no cabeçalho referer.

Uma coisa importante a observar é que isso não se aplica apenas a situações de login e assunção de conta. Vi más configurações em:

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

Pré-Assunção de Conta

Um dos outros problemas mais comuns que vejo é quando os aplicativos permitem "Entrar com X", mas também nome de usuário/senha. 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 endereço de e-mail da vítima e a senha do invasor antes que a vítima se registre. Se a vítima então tentar se registrar ou entrar com um terceiro, como o Google, é possível que o aplicativo faça uma pesquisa, veja que o e-mail já está registrado e, em seguida, vincule a conta do Google deles à conta criada pelo invasor. Isso é uma "pré-assunção de conta" em que um invasor 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 com um endereço de e-mail da vítima. O mesmo problema acima pode existir, mas você estaria atacando-o na outra direção e obtendo acesso à conta da vítima para uma assunção de conta.

Divulgação de Segredos

É muito importante reconhecer quais dos muitos parâmetros do OAuth são secretos e protegê-los. Por exemplo, vazar o client_id é perfeitamente normal e necessário, mas vazar o client_secret é perigoso. Se isso for vazado, o invasor 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 o client_secret de sua aplicação, fará uma solicitação do servidor para recuperar um access_token em seu nome, o que permitirá que eles acessem as permissões que 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 alguma engenharia social, eles também podem adicionar mais escopos à autorização do OAuth e tudo parecerá legítimo, já que a solicitação virá do aplicativo cliente confiável.

Bruteforce de Segredo do Cliente

Você pode tentar forçar o segredo do cliente de um provedor de serviços com o provedor de identidade para tentar roubar contas.
A solicitação para BF pode ser 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]

Vazamento de Cabeçalho Referer + Código de Estado

Depois que o cliente tem o código e o estado, se ele for refletido dentro do cabeçalho Referer quando ele navega para uma página diferente, então há uma vulnerabilidade.

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 Eterno

O código de autorização deve viver apenas por algum tempo para limitar a janela de tempo em que um invasor pode roubá-lo 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 e Mensagens de Postagem para vazar valores de código e estado

AWS Cognito

Neste relatório de recompensa por bugs: https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/ você pode ver que o token que o AWS Cognito retorna ao usuário pode ter permissões suficientes para sobrescrever os dados do usuário. Portanto, se você puder alterar o e-mail do usuário para um e-mail de usuário diferente, poderá 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 obter informações mais detalhadas sobre como abusar do AWS Cognito, consulte:

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

De acordo com este artigo, era possível fazer com que a 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 um passo posterior, o prompt perguntaria ao usuário se ele queria dar acesso a esse host do atacante.

Para contornar esse prompt, era possível abrir uma guia para iniciar o fluxo Oauth que definiria esse cookie RU usando o returnUrl, fechar a guia antes que o prompt fosse exibido e abrir uma nova guia sem esse valor. Então, o prompt não informará sobre o host do atacante, mas o cookie será definido para ele, então o token será enviado para o host do atacante na redireção.

Parâmetros SSRFs

Uma das URLs ocultas que você pode perder é o endpoint de registro de cliente dinâmico. Para autenticar com sucesso os usuários, os servidores OAuth precisam saber detalhes sobre o aplicativo cliente, como "client_name", "client_secret", "redirect_uris" e assim por diante. Esses detalhes podem ser fornecidos por meio de configuração local, mas os servidores de autorização OAuth também podem ter um endpoint de registro especial. Esse endpoint 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 ser possíveis alvos para Server Side Request Forgery. Ao mesmo tempo, a maioria dos servidores que testamos não resolvem essas URLs imediatamente quando recebem uma solicitação de registro. Em vez disso, eles apenas salvam esses parâmetros e os usam posteriormente durante o fluxo de autorização do 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 do cliente. Depois de registrar um cliente, você pode tentar chamar o ponto de extremidade de autorização do OAuth ("/authorize") usando seu novo "client_id". Após o login, o servidor solicitará que você aprove 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 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 conjunto de chaves JSON do cliente [JWK]. Este conjunto de chaves é necessário no servidor para validar solicitações assinadas feitas para o ponto de extremidade de token ao usar JWTs para autenticação do cliente [RFC7523]. Para testar o SSRF neste parâmetro, registre um novo aplicativo cliente com um "jwks_uri" malicioso, execute 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 de SSRF cega, pois o servidor espera uma resposta JSON adequada.

  • sector_identifier_uri - Esta URL faz referência a 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 executar a autorização para este cliente no servidor. Como ele precisa saber os redirect_uris para concluir o fluxo de autorização, isso forçará o servidor a fazer uma solicitação para o seu setor_identifier_uri malicioso.

  • request_uris - Uma matriz 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 (consulte https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.6.2).

    Mesmo que o registro dinâmico do cliente não esteja habilitado ou exija autenticação, podemos tentar o 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 a 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 permitem apenas URLs na lista branca que foram pré-registradas 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 de provedores OAuth

Se a plataforma que você está testando for um provedor OAuth, leia isto para testar possíveis condições de corrida.

Referências

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