How to Become a Rockstar

Ricardo Iramar dos Santos
9 min readDec 29, 2017

--

Meu objetivo inicial era levantar um grana extra e em segundo lugar aprender mais sobre web security então escolhi um Bug Bounty Program no HackerOne que era recente, para ter mais chances de achar algo, e que pagava razoavelmente bem. Infelizmente acabei atingindo somente o segundo objetivo.

Não me lembro exatamente como foi, mas acabei escolhendo um grande serviços de streaming o qual já utilizava a versão gratuita por um bom tempo. De cara achei um XSS que só funcionava no IE 7 e ainda por cima era fora de escopo do programa. Depois achei algo que prefiro nem comentar pois além de também ser fora do escopo e me custaram -5 pontos de reputação. Se você um dia for brincar de Bug Hunter leia com muita atenção a descrição do programa e principalmente o escopo para não fazer as mesmas merdas que fiz.

Puto da vida com a merda que tinha feito e com sangue nos olhos dediquei um pouco mais de tempo e acabei achando algo muito mais interessante o qual vou descrever neste write-up. Na verdade o que mais me motivou foi a resposta abaixo do programa para o meu report.

Se você realmente quer brincar de Bug Hunter se prepare para se deparar com vários Duplicates. Duplicate é quando alguém já reportou o mesmo problema que você encontrou porém ainda não foi corrigido. Resumindo, quem reportou primeiro recebe o reward e quem achou o Duplicate leva nada.

Até que de começo me fez sentido o critério de quem reporta primeiro leva a grana, mas lendo o report do garoto que enviou primeiro vi claramente que a vulnerabilidade era a mesma porém a forma na qual eu demonstrava o impacto era completamente diferente da forma dele. Então pedi para tratar o meu report separado do garoto e recebi essa resposta abaixo.

Esse foi meu segundo vacilo que me deixou mais puto ainda. Eu havia entendido da mensagem acima que na verdade eles iriam tratar os reports juntos mas no final o reward seria de acordo com o impacto demonstrado em cada report. No final da história eu não recebi nada e o garoto recebeu $250 trumps. Para se ter uma ideia esse report aqui https://hackerone.com/reports/235200 descreve exatamente o mesmo problema e o cara recebeu $1000 trumps. Acredito que se eu tivesse reportado primeiro poderia ter sido recompensado com um valor maior seguindo a lógica de quanto maior o impacto maior a recompensa.

Você deve estar se perguntando porque eu escondi o nome do programa nas imagens. Como se eu já não estivesse descontente o suficiente com o que aconteceu ao final pedi para fazer o disclose do meu report e a resposta segue abaixo.

Não posso fazer o disclose mas posso falar sobre o problema sem citar o programa apesar que deve ser fácil deduzir de qual serviço de streaming estou falando.

Chega de blablabla e vamos para o que interessa. Para entender como explorei essa falha precisamos primeiro dar uma rápida passada no que é Cross-Origin Resource Sharing ou mais conhecido como CORS. Veja esse código JavaScript abaixo.

O funcionamento é simples. Um usuário acessa uma página do domínio FOO que busca conteúdo em outro domínio BAR através de uma chamada XMLHttpRequest.

Esta chamada em BAR é de um conteúdo protegido por isso é necessário enviar as credenciais (withCredentials = true) mais conhecido como cookie de sessão.

Para este exemplo vejamos como seria a HTTP request e o response.

Na request o cabeçalho Host determina o domínio de destino da chamada e Origin de onde partiu. Lembrando que o cabeçalho Origin gerado pelo browser não tem como ser “spoofado”. Na resposta os cabeçalhos Access- Control-Allow indicam para o browser qual domínio o payload deve ser liberado (Access-Control-Allow-Origin: http://foo.example) e que chamadas autenticadas são permitidas (Access-Control-Allow-Credentials: true). Se quiser saber mais em detalhes sobre CORS sugiro a leitura da W3C Recommendation que pode ser encontrada neste endereço https://www.w3.org/TR/cors/.

Tendo em mente esse exemplo vamos dar uma olhada em uma simples request e response do Accounts Service deste serviço de streaming que chamaremos de XPTO daqui para frente.

O XPTO Accounts Service esta hospedado em https://accounts.xpto.com como podemos ver no cabeçalho Host e a origem da chamada é https://accounts-xpto.com. Na resposta é possível ver através dos cabeçalhos que o domínio de origem é aceito. Perceba que são domínios totalmente distintos somente porque no primeiro temos um ponto (“.xpto”) e no segundo um traço (“-xpto”). Sempre que o Burp encontra esses cabeçalhos ele cria uma Issue com o nome Cross-origin resource sharing e Severity igual à Information.

Após diversas chamadas concluí que o XPTO Accounts Service estava gerando os cabeçalhos Access-Control-Allow dinamicamente de acordo com o domínio definido pelo cabeçalho Origin da request. Porém nem todos os domínios eram aceitos somente os que seguiam o padrão “accounts<qualquercaracter>xpto.com”.

Essa é a falha que encontrei após o garoto ter reportado. Muito provavelmente quem fez o filtro dinâmico escreveu uma regexp e não se atentou que o ponto na verdade é um carácter especial que casa com qualquer caractere e o mesmo deveria ser “escapado” com uma contra barra (\.).

Primeira coisa que fiz para provar que o ataque era possível foi verificar a disponibilidade do domínio “accounts-xpto.com” o qual realmente poderia ser cadastrado por qualquer um.

Não precisava ser necessariamente “accounts-xpto.com” pois no lugar do traço poderia ser qualquer caractere entretanto escolhi esse domínio pois acredito que não levantaria muito suspeita.

Já o garoto escolheu “accountsexpto.com” o qual realmente registrou gastando dinheiro a toa. No meu caso, simplesmente alterei o arquivo hosts apontando “accounts-xpto.com” para o IP de uma VM em minha rede local para simular uma situação real onde um atacante registraria esse domínio. Veja como o garoto demonstrou o impacto.

O garoto simplesmente indicou o impacto com as imagens acima informando que alguns dados pessoais vazavam para uma um domínio de controle do atacante. Ele realmente não sabia para que o CORS servia e nem o que havia encontrado.

O XPTO Accounts Service na verdade é um Authorization Server (Oauth 2.0) que serve para emitir access tokens para seus clientes após um usuário se autenticar com sucesso. OAuth 2.0 é um authorization framework que habilita aplicações terceiras obtenha acesso a serviços em nome de seus usuários. Fica inviável falar em detalhes aqui sobre Authorization Server ou Oauth 2.0 devido a complexidade, portanto se quiser saber mais a respeito, aconselho a ler a RFC-6749 (https://tools.ietf.org/html/rfc6749).

Precisamos primeiro entender para quer serve o Implicit Grant Flow e Authorization Code Flow no Oauth 2.0 antes de seguir em frente. Vejamos primeiro Implicit Grant Flow.

O Implicit Grant Flow é utilizado por aplicações implementados totalmente em JavaScript, isto é, são executados no browser do usuário. A vantagem é que não há necessidade de um servidor de aplicação de terceiros porém o access token fica muito mais exposto. Esse Flow é definido via o parâmetro response_type=token na primeira chamado ao XPTO Accounts Service.

Vejamos agora o Authorization Code Flow.

A desvantagem do Authorization Code Flow é a vantagem do Implicit Grant Flow, isto é, precisa de um servidor de aplicação de terceiros que irá fazer as chamadas pelo usuário no Authorization Server. Sua vantagem é que o access token não é exposto como Implicit Grant Flow e os mesmos podem ser renovados através do refresh token. Esse Flow é definido via o parâmetro response_type=code na primeira chamado ao XPTO Accounts Service.

Precisei falar sobre esses dois Flows pois o Implicit Grant é mais comumente usado por essa aplicação e, por ser totalmente em JavaScript, recebe o o access token do XPTO Accounts Service via HTML bookmarks (https://application.com/callback#access_token=NwAExz...BV3O2Tk&token_type=Bearer&expires_in=3600&state=123). HTML bookmarks é tudo que vem depois do sinal de hashtag (#) e fica contido somenre no browser portanto não é enviado para o servidor.

Já no Authorization Code Flow como existe uma aplicação fazendo as chamadas pelo usuário o access token é recebido via query parameter (https://application.com/callback?code=NApCCg..BkWtQ&state=profile%2Factivity).

É importante entender essa diferença pois do ponto de vista de um atacante a forma de obter o access token usando o Authorization Code Flow é diferente do Implicit Grant Flow. Via Authorization Code Flow (response_type=code) o access token é enviado direto para o domínio de controle do atacante, já via Implicit Grant Flow o mesmo precisa ser manipulado via JavaScript.

Para demonstrar que era possível pegar o access token criei uma página simples em HTML que fazia um alert(body) da resposta do XPTO Accounts Service.

De posse do access token fiz um chamado no /me da API do serviço para demonstrar que era possível obter dados pessoais do usuário.

Na real eu poderia efetuar qualquer API desta lista abaixo em nome do usuário.

Quando estava olhando a lista de APIs a de “follow” chamou minha atenção. E se eu fizesse vários usuários seguir um artista ou usuário especifico? Como?

Este mesmo serviço de streaming tem um outro site somente para tirar dúvidas. Na verdade é um fórum chamado Lithium (https://www.lithium.com) que várias empresas usam. Esse fórum utiliza o mesmo Authorization Server que os demais sistemas, portanto obrigatoriamente você precisa estar logado para acessa-lo.

Antigamente usuários normais podiam usar a tag iframe em suas mensagens nesse fórum mas agora somente usuários com o perfil Rockstar. Apesar de o Bug Bounty Program não permitir Social Engineering entrei em contato com um Rockstar e o “convenci” de postar um iframe, mesmo ele não sabendo exatamente do que se tratava, com esse conteúdo abaixo para provar que o meu cenário malicioso era possível.

Em resumo o caso de uso inteiro do ataque funcionária da seguinte forma.

Perceba que esse código JavaScript faz uma chamada para pegar o access token e em seguida faz um POST em outra página para enviar o access token para o domínio sobre controle do atacante. O código da página que recebe o access token peguei no seguinte endereço https://gist.github.com/magnetikonline/650e30e485c0f91f2f40.

Esse log prova que o access token foi enviado para o domínio de controle do atacante. Na verdade não era nem preciso enviar o access token. Na mesma página maliciosa um atacante poderia escrever um código JavaScript para fazer as chamadas nas APIs como seguir um artista ou usuário.

--

--