* Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
Primeiro, existem diferentes tipos de resposta que você pode usar no fluxo OAuth. Essas respostas concedem o **token para fazer login como os usuários ou as informações necessárias para fazê-lo**.
1.**`code` + `state`**. O **código** é usado para **chamar o servidor do provedor OAuth** para obter um token. O parâmetro **state** é usado para verificar se o **usuário correto está fazendo a chamada**. É responsabilidade do cliente OAuth validar o parâmetro de estado antes de fazer a chamada do lado do servidor para o provedor OAuth.
2.**`id_token`**. É um JSON Web Token **(JWT) assinado** usando um certificado público do provedor OAuth para verificar se a identidade fornecida é realmente quem ela afirma ser.
3.**`token`**. É um **token de acesso** usado na API do provedor de serviços.
Existem diferentes modos que o fluxo de autorização pode usar para fornecer os códigos ou tokens para o site no fluxo OAuth, estes são quatro dos mais comuns:
1.**Query**. Enviando parâmetros de consulta como um redirecionamento de volta para o site (`https://example.com/callback?code=xxx&state=xxx`). Usado para `code+state`. O **código** só pode ser **usado uma vez** e você precisa do **segredo do cliente OAuth** para **adquirir um token de acesso** ao usar o código. 
1. [Este modo não é recomendado para tokens](https://openid.net/specs/oauth-v2-multiple-response-types-1\_0-09.html#id\_token) pois **os tokens podem ser usados várias vezes e não devem acabar em logs do servidor ou similares**. A maioria dos provedores OAuth não suporta esse modo para tokens, apenas para código. Exemplos:
*`response_mode=query` é usado pela Apple.
*`response_type=code` é usado pelo Google ou Facebook.
2.**Fragment**. Usando um **redirecionamento de fragmento** (`https://example.com/callback#access_token=xxx`). Neste modo, a parte do fragmento da URL não acaba em nenhum log do servidor e só pode ser alcançada do lado do cliente usando javascript. Este modo de resposta é usado para tokens. Exemplos:
*`response_mode=fragment` é usado pela Apple e Microsoft.
*`response_type` contém `id_token` ou `token` e é usado pelo Google, Facebook, Atlassian e outros.
3.**Web-message**. Usando **postMessage para uma origem fixa do site**:\
Se suportado, muitas vezes pode ser usado para todos os diferentes tipos de resposta. Exemplos:
*`response_mode=web_message` é usado pela Apple.
*`redirect_uri=storagerelay://...` é usado pelo Google.
*`redirect_uri=https://staticxx.facebook.com/.../connect/xd_arbiter/...` é usado pelo Facebook.
4.**Form-post**. Usando um post de formulário para um `redirect_uri` válido, um **pedido POST regular é enviado de volta para o site**. Isso pode ser usado para código e tokens. Exemplos:
*`response_mode=form_post` é usado pela Apple.
*`ux_mode=redirect&login_uri=https://example.com/callback` é usado pelo Google Sign-In (GSI).
A especificação OAuth recomenda um parâmetro `state` em combinação com um `response_type=code` para garantir que o usuário que iniciou o fluxo também é o que está usando o código após o fluxo OAuth para emitir um token.
No entanto, se o **valor do `state` for inválido**, o **`code` não será consumido** porque é responsabilidade do site (o final) validar o estado. Isso significa que se um atacante puder enviar um link de fluxo de login para uma vítima contaminada com um `state` válido do atacante, o fluxo OAuth falhará para a vítima e o `code` nunca será enviado ao provedor OAuth. O código ainda será possível de usar se o atacante puder obtê-lo.
O autor da pesquisa chamou de **caminhos não felizes** aqueles em que o usuário faz login via OAuth e é redirecionado para URLs incorretas. Isso é útil porque se o cliente receber o token ou um estado+codigo válido **mas não chegar à página esperada**, essa **informação não será consumida corretamente** e se o atacante encontrar uma maneira de **extrair essa informação** do "caminho não feliz", ele poderá **assumir o controle da conta**.
Por padrão, o fluxo OAuth chegará ao caminho esperado, no entanto, pode haver algumas **configurações incorretas** potenciais que permitiriam a um atacante **criar uma solicitação OAuth inicial específica** que fará com que o **usuário chegue a um caminho não feliz após fazer login**.
A [**especificação**](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-19#section-2.1) **** indica estritamente que a URL de redirecionamento deve ser estritamente comparada com a definida, não permitindo alterações além da aparência ou não da porta. No entanto, alguns endpoints permitiam algumas modificações:
Alguns provedores OAuth **permitem a adição de dados adicionais** ao caminho para `redirect_uri`. Isso também viola a especificação da mesma forma que para "Mudança de caso de URI de redirecionamento". Por exemplo, tendo um URI de redirecionamento `https://example.com/callback`, enviando:
Alguns provedores OAuth **permitem a adição de parâmetros de consulta ou fragmento** ao `redirect_uri`. Você pode usar isso ao acionar um caminho não feliz, fornecendo os mesmos parâmetros que serão adicionados à URL. Por exemplo, tendo um redirecionamento uri `https://example.com/callback`, enviando:
acabaria nestes casos como um redirecionamento para `https://example.com/callback?code=xxx&code=real-code`. Dependendo do site que recebe **múltiplos parâmetros com o mesmo nome, isso também poderia desencadear um caminho não feliz**. O mesmo se aplica a `token` e `id_token`:
Os caminhos felizes do OAuth são aqueles em que tudo ocorre conforme o esperado. No entanto, existem casos em que o fluxo pode ser interrompido, como quando há múltiplos parâmetros com o mesmo nome na URL de retorno. Isso pode resultar em uma URL de retorno como `https://example.com/callback#id_token=xxx&id_token=real-id_token`. Dependendo do **javascript que busca os parâmetros de fragmento quando há múltiplos parâmetros com o mesmo nome**, isso também pode resultar em um caminho não feliz.
Ao coletar todas as URLs de login contendo os valores `redirect_uri`, também é possível testar se outros valores de redirect-uri são válidos. Dos 125 fluxos de login do Google que salvei dos sites que testei, 5 sites tinham a página inicial também como um `redirect_uri` válido. Por exemplo, se `redirect_uri=https://auth.example.com/callback` fosse o valor usado, nesses 5 casos, qualquer um desses valores também seria válido:
Isso foi especialmente interessante para os sites que realmente usavam `id_token` ou `token`, já que `response_type=code` ainda terá o provedor OAuth validando o `redirect_uri` na última etapa da dança do OAuth ao adquirir um token.
**Neste exemplo, o último caminho não feliz em que o token/código estava sendo enviado estava enviando uma mensagem de solicitação de postagem vazando location.href.**\
Um exemplo foi um SDK de análise para um site popular que foi carregado em sites:
1. O atacante envia ao usuário um **link criado** que foi preparado para **resultar em um caminho não feliz** na dança do OAuth.
2. A vítima **clica** no link. Uma nova aba é aberta com um fluxo de **login** com um dos provedores OAuth do site sendo explorado.
3. O caminho não feliz é acionado no site sendo explorado, o **ouvinte de postMessage vulnerável é carregado na página em que a vítima pousou, ainda com o código ou tokens na URL**.
4. A **aba original** enviada pelo atacante envia um monte de **postMessages** para a nova aba com o site para fazer com que o ouvinte de postMessage vaze a URL atual.
5. A aba original enviada pelo atacante então **ouve a mensagem enviada para ela**. Quando a URL retorna em uma mensagem, o **código e token são extraídos** e enviados ao atacante.
6.**Atacante faz login como a vítima** usando o código ou token que acabou no caminho não feliz.
Este tinha um **iframe** carregado na **página onde a dança do OAuth terminou**. O **nome** do **iframe** era uma **versão JSON-stringified do objeto `window.location`**. Esta é uma maneira antiga de transferir dados entre domínios, já que a página no iframe pode ter seu próprio `window.name` definido pelo pai:
Se você tiver um **XSS** em um **domínio** em uma janela, essa janela pode então **alcançar outras janelas da mesma origem** se houver uma relação de pai/filho/opener entre as janelas.
Isso significa que um invasor poderia **explorar o XSS para carregar uma nova guia** com o **link OAuth criado** que terminará no **caminho que carrega o iframe com o token no nome**. Então, a partir da página explorada pelo XSS, será possível **ler o nome do iframe** porque ele tem um **opener sobre a página pai dos iframes** e exfiltrá-lo.
Também iniciei um script em um intervalo para verificar se o link foi aberto e se o iframe que eu queria alcançar está lá para obter o `window.name` definido no iframe com a mesma origem que o iframe na página do invasor:
O segundo exemplo foi um **iframe** carregado no **caminho não feliz** com um XSS **usando postMessage**, mas **as mensagens só eram permitidas do `parent`** que o carregou. O **`location.href` foi enviado para o iframe quando ele pediu `initConfig`** em uma mensagem para a janela `parent`.
# Oauth Happy Paths: XSS, Iframes and Post Messages to Leak Code and State Values
## Introduction
This document explains how to exploit Oauth Happy Paths to leak code and state values using XSS, iframes and post messages.
## Oauth Happy Paths
Oauth Happy Paths are the different flows that an Oauth client can follow to obtain an access token from an Oauth provider. These flows are defined in the Oauth specification and are implemented by Oauth providers.
## Exploiting Oauth Happy Paths
An attacker can exploit Oauth Happy Paths to leak code and state values by injecting malicious code into the Oauth provider's login page. This can be done using XSS, iframes and post messages.
### XSS
XSS can be used to inject malicious code into the Oauth provider's login page. This code can then be used to steal the user's access token and other sensitive information.
### Iframes
If the Oauth provider's login page is vulnerable to clickjacking, an attacker can use iframes to load the login page and steal the user's access token and other sensitive information.
### Post Messages
Post messages can be used to communicate between different windows or iframes. An attacker can use post messages to steal the user's access token and other sensitive information.
Oauth Happy Paths can be exploited to leak code and state values using XSS, iframes and post messages. It is important for Oauth providers to implement proper security measures to prevent these attacks.
Neste caso, o **atacante carrega um iframe com a página vulnerável de XSS de post-message** e **explora** o **XSS** para carregar **JS arbitrário**. Este **JS** irá **abrir** uma **guia** com o **link OAuth**. Após o login, a página final contém o token na URL e carregou um iframe (o iframe vulnerável de post-message XSS).
Em seguida, o **JS arbitrário** (do XSS explorado) tem um **abridor para essa guia**, então ele **acessa o iframe** e faz com que ele **peça ao pai pelo `initConfig`** (que contém a **URL com o token**). A página pai **fornece-o ao iframe**, que também é comandado para **vazá-lo**.
1. Criar uma **página maliciosa** que esteja incorporando um **iframe do sandbox**, anexar um **onload** para **disparar um script quando o iframe é carregado**.
2. Como a **página maliciosa é então o pai** do iframe, ela poderia **enviar uma mensagem para o iframe para carregar nosso script** na origem do sandbox usando **postMessage (XSS)**:
Eu também iniciei um script em um intervalo para **verificar se o link foi aberto e se o iframe que eu queria alcançar estava lá**, para executar javascript dentro dele do meu iframe para a janela principal. Em seguida, anexei um ouvinte de postMessage que passou a mensagem de volta para o meu iframe na janela maliciosa:
4. A página do atacante que tinha o iframe carregado pode então ouvir a mensagem que enviei do ouvinte de postMessage injetado no iframe da janela principal:
Este gadget acabou sendo o mais divertido. Há algo satisfatório em enviar a vítima para algum lugar e depois pegar dados sensíveis de um local diferente.
A janela principal se comunicaria com este iframe usando postMessage para enviar dados de rastreamento que seriam salvos no localStorage da origem em que o `storage.html` estava localizado:
Se você pudesse conversar com essa origem de alguma forma e fazê-la enviar o conteúdo, o `location.href` poderia ser obtido a partir desse armazenamento. O ouvinte de postMessage para o serviço tinha uma lista de bloqueio e uma lista de permissão de origens. Parece que o serviço de análise permitiu que o site definisse quais origens permitir ou negar:
Além disso, se você tivesse uma origem válida com base na `allowList`, também seria capaz de solicitar uma sincronização, o que lhe daria todas as alterações feitas no localStorage nesta janela enviadas para você quando foram feitas.
No site que tinha esse armazenamento carregado no caminho não feliz da dança do OAuth, nenhuma origem da `allowList` foi definida; **isso permitiu que qualquer origem falasse com o ouvinte de postMessage** se a origem fosse o `parent` da janela:
1. Eu criei uma página maliciosa que incorporava um iframe do contêiner de armazenamento e anexei um `onload` para acionar um script quando o iframe é carregado.
2. Como a página maliciosa agora era o pai do iframe e nenhuma origem foi definida na `allowList`, a página maliciosa poderia enviar mensagens para o iframe para dizer ao armazenamento para enviar mensagens para quaisquer atualizações no armazenamento. Eu também poderia adicionar um ouvinte à página maliciosa para ouvir quaisquer atualizações de sincronização do armazenamento:
4. A vítima clicaria no link, passaria pela dança do OAuth e acabaria no caminho não feliz carregando o script de rastreamento e o iframe de armazenamento. O iframe de armazenamento recebe uma atualização de `last-url`. O evento `window.storage` seria acionado no iframe da página maliciosa, uma vez que o localStorage foi atualizado, e a página maliciosa que agora estava recebendo atualizações sempre que o armazenamento mudava receberia uma postMessage com a URL atual da vítima:
Como o próprio serviço de análise tinha um programa de recompensas por bugs, também fiquei interessado em ver se poderia encontrar uma maneira de vazar URLs também para os sites que haviam configurado origens adequadas para o iframe de armazenamento.
Quando comecei a procurar o domínio `cdn.analytics.example.com` online sem a parte do cliente, notei que esse CDN também continha imagens enviadas pelos clientes do serviço:
Não é ótimo. O CDN adicionou um cabeçalho `Content-Security-Policy: default-src 'self'` para tudo sob `img/`. Você também pode ver que o cabeçalho do servidor mencionou o S3 - revelando que o conteúdo foi carregado em um bucket S3:
Uma peculiaridade interessante do S3 é que os diretórios não são realmente diretórios no S3; o caminho antes da chave é chamado de "prefixo". Isso significa que o S3 não se importa se `/` são codificados em URL ou não, ele ainda servirá o conteúdo se você codificar em URL cada barra na URL. Se eu mudasse `img/` para `img%2f` na URL, a imagem ainda seria exibida. No entanto, nesse caso, o cabeçalho CSP foi removido e o XSS foi acionado:
Eu então carreguei um SVG que criaria a mesma forma de manipulador de armazenamento e ouvinte de postMessage como o `storage.html` regular, mas com uma `allowList` vazia. Isso me permitiu fazer o mesmo tipo de ataque mesmo em sites que haviam definido corretamente as origens permitidas que poderiam falar com o armazenamento.
Eu poderia então utilizar a mesma metodologia do exemplo #1, mas em vez de colocar o `storage.html` em um iframe, eu poderia colocar o SVG com a barra codificada em URL:
A ideia de olhar para bugs de configuração incorreta em terceiros era principalmente para confirmar que existem várias maneiras de vazar os tokens e, como o terceiro tinha um programa de recompensas por bugs, este era apenas um receptor diferente para o mesmo tipo de bug, a diferença sendo que o impacto era para todos os clientes do serviço de análise. Neste caso, o cliente do terceiro na verdade tinha a capacidade de configurar corretamente a ferramenta para não vazar dados para o atacante. No entanto, como os dados sensíveis ainda eram enviados para o terceiro, foi interessante ver se havia alguma maneira de contornar completamente a configuração adequada da ferramenta pelo cliente.
O último exemplo foi baseado em um chat-widget que estava presente em todas as páginas de um site, até mesmo nas páginas de erro. Havia vários ouvintes de postMessage, um deles sem uma verificação de origem adequada que permitia apenas iniciar o pop-up do chat. Outro ouvinte tinha uma verificação rigorosa de origem para que o chat-widget recebesse uma chamada de inicialização e o token da API de chat atual que estava sendo usado pelo usuário atual.
1. Se um chat-api-token existir no localStorage do widget de chat, ele enviará o api-token para seu pai usando postMessage. Se nenhum chat-api-token existir, ele não enviará nada.
2. Quando o iframe é carregado, ele enviará uma mensagem postMessage com `{"type": "chat-widget", "key": "init"}` para seu pai.
1. Se nenhum chat-api-token tiver sido enviado ainda, o widget de chat criará um e o colocará no localStorage de sua própria origem e o enviará por postMessage para a janela pai.
2. A janela pai fará uma chamada de API para o serviço de chat. O endpoint da API estava restrito pelo CORS ao site específico configurado para o serviço. Você precisava fornecer um cabeçalho `Origin` válido para a chamada de API com o chat-api-token para permitir que a solicitação fosse enviada.
3. A chamada de API da janela principal conteria `location.href` e o registraria como a "página atual" do visitante com o chat-api-token. A resposta então conteria tokens para se conectar a um websocket para iniciar a sessão de chat:
Neste exemplo, percebi que o anúncio do chat-api-token sempre seria anunciado ao pai do iframe do widget de chat e, se eu obtivesse o chat-api-token, poderia fazer uma solicitação do lado do servidor usando o token e, em seguida, adicionar meu próprio cabeçalho `Origin` artificial à chamada de API, já que um cabeçalho CORS só importa para um navegador. Isso resultou na seguinte cadeia:
1. Criei uma página maliciosa que incorpora um iframe do widget de chat, adicionou um ouvinte de postMessage para ouvir o chat-api-token. Além disso, acionei um evento para recarregar o iframe se eu não tivesse recebido o api-token em 2 segundos. Isso foi para garantir que eu também suportasse as vítimas que nunca iniciaram o chat e, como eu poderia acionar a abertura do chat remotamente, primeiro precisava do chat-api-token para começar a pesquisar os dados no chat-API do lado do servidor.
3. A função `launchChatWindowByPostMessage()` continuamente enviará uma mensagem postMessage para a janela principal, se aberta, para lançar o widget de chat:
4. Quando a vítima clicou no link e acabou na página de erro, o chat seria iniciado e um chat-api-token seria criado. Minha recarga do iframe do widget de chat na página maliciosa obteria o `api-token` por meio de postMessage e eu poderia então começar a procurar no API a URL atual da vítima:
5. A página do lado do servidor em `https://fetch-server-side.attacker.test/?token=xxx` faria a chamada de API com o cabeçalho Origin adicionado para fazer o Chat-API pensar que eu estava usando-o como uma origem legítima:
6. Quando a vítima clicou no link e passou pela dança do OAuth e pousou na página de erro com o token adicionado, o widget de chat de repente apareceria, registraria a URL atual e o atacante teria o token de acesso da vítima.
Ainda existem diferentes tipos de gadgets esperando para serem encontrados. Aqui está um desses casos que não consegui encontrar na natureza, mas poderia ser uma maneira potencial de obter a URL para vazar usando qualquer um dos modos de resposta disponíveis.
Como todos os tipos de resposta `web_message` não podem validar nenhum caminho da origem, qualquer URL em um domínio válido pode receber a postMessage com o token. Se houver algum tipo de ouvinte de postMessage-proxy em uma das páginas do domínio, que recebe qualquer mensagem enviada para ele e envia tudo para seu `opener`, posso fazer uma cadeia de window.open dupla:
Eu poderia usar qualquer um dos modos de resposta `web_message` para enviar o token do provedor OAuth para a origem válida `https://example.com`, mas o endpoint enviaria o token para `opener`, que é a página do atacante.
Esse fluxo pode parecer improvável e requer dois cliques: um para criar um relacionamento de abertura entre o atacante e o site e o segundo para iniciar o fluxo OAuth tendo o site legítimo como o abridor do pop-up OAuth.
* Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e para o** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).