História de um estudante do último ano do ensino médio que, com tempo livre por causa da Covid, foi caçar bug bounties e recebeu US$ 35.000 por um bug bounty de páginas privadas do GitHub.
Ele reportou o bug bounty de páginas privadas do GitHub e havia dois bônus de CTF.
-
US$ 10.000: ler a flag em
flag.private-org.github.iosem interação do usuário. Se for possível ler essa flag a partir de uma conta fora da organizaçãoprivate-org, há um bônus adicional de US$ 5.000. -
US$ 5.000: ler a flag em
flag.private-org.github.iocom interação do usuário.
Fluxo de autenticação
Como o GitHub Pages é hospedado em um domínio separado, github.io, os cookies de autenticação de github.com não são enviados ao servidor de páginas privadas. Portanto, a autenticação de páginas privadas não consegue identificar o usuário sem integração adicional com github.com. Por isso, o GitHub criou um fluxo de autenticação personalizado.
-
Ao visitar uma página privada, o servidor verifica a existência do cookie
__Host-gh_pages_token. -
Se o cookie não existir ou for inválido, o servidor da página privada redireciona para
https://github.com/login. -
Esse redirecionamento também define um nonce no cookie
__Host-gh_pages_session.- Como esse cookie usa o prefixo
__Host-, ele impede que seja definido via JavaScript fora do domínio do host.
- Como esse cookie usa o prefixo
-
/loginredireciona para/pages/auth?nonce=&page_id=&path=. -
Ali, é criado um cookie de autenticação temporário que é passado no parâmetro
tokenparahttps://pages-auth.github.com/redirect. -
/redirectencaminha parahttps://repo.org.github.io/__/auth. -
Esse endpoint final define os cookies de autenticação
__Host-gh_pages_tokene__Host-gh_pages_idno domíniorepo.org.github.io. -
Aqui, o
noncede__Host-gh_pages_session, definido anteriormente, também é verificado.
O caminho original da requisição e o ID da página são armazenados respectivamente nos parâmetros de query path e page_id, e o nonce também é armazenado no parâmetro nonce.
Exploração
Retorno CRLF
-
A primeira vulnerabilidade era uma injeção de CRLF no parâmetro
page_iddehttps://repo.org.github.io/__/auth. -
Foi descoberto que o parsing de
page_idignorava espaços em branco e que esse valor era definido diretamente no headerSet-Cookie. -
Era possível quebrar o parsing com uma injeção CRLF tradicional, mas sem outros impactos.
-
Como o header
Location:vinha depois do headerSet-Cookie, apesar de ser um redirecionamento 302 o header Location era ignorado e o corpo era renderizado.
Ataque
-
Ao olhar o código do GitHub Enterprise, ele descobriu que o servidor de páginas privadas era implementado com openresty nginx.
-
Ele conseguiu XSS adicionando um byte nulo. Como esse null byte precisava vir no início do body, não era possível fazer um ataque de injeção de header.
-
A partir daí, tornou-se possível executar código JavaScript arbitrário no domínio da página privada.
-
Agora só faltava encontrar uma forma de contornar o nonce.
Burlando o nonce
-
Pela observação, foi descoberto que páginas privadas irmãs da mesma organização podiam definir cookies umas para as outras.
-
Um cookie definido em
private-org.github.ioé enviado paraprivate-page.private-org.github.io. -
Se desse para contornar a proteção do prefixo
__Host-, seria fácil burlar o nonce. -
Nem todos os navegadores suportam isso, e o IE não suporta o prefixo
__Host-. -
Mas, ao procurar uma forma melhor, surgiu uma ideia interessante.
-
Ao verificar como os cookies tratavam maiúsculas e minúsculas, foi descoberto que
__HOSTe__Hosteram tratados de forma diferente, e que o GitHub private pages ignorava letras maiúsculas ao fazer o parsing do cookie. -
Assim, passou a ser possível definir o nonce via JavaScript.
-
Isso rendeu o bônus de US$ 5.000.
Envenenamento de cache
-
A resposta do endpoint
/__/auth?é armazenada em cache pelo valor inteiro dopage_idforjado. -
Com isso, se fosse possível envenenar o cache por meio do payload de XSS, até usuários sem interação seriam afetados.
-
Se um atacante comprometer
unprivileged.org.github.iopara envenenar a autenticação, o payload de XSS fica em cache. -
Como os cookies são compartilhados no domínio pai
org.github.io, o atacante também consegue atacarprivileged.org.github.io.
Páginas privadas públicas
-
Para receber o bônus de US$ 15.000, era necessário fazer esse ataque com um usuário que não pertencesse à organização.
-
Isso era possível por causa de uma configuração incorreta que ativava páginas privadas em um repositório público.
- Ou seja, criar páginas em um repositório privado e depois mudar o repositório para público.
-
Nessas páginas privadas mal configuradas, todos os usuários entram no fluxo de autenticação, e usuários fora da organização passam a ter permissão de leitura.
Ainda não há comentários.