Redirecionamento aberto no app Harvest permitia roubo de token OAuth de contas Microsoft
(eval.blog)- No fluxo de conexão OAuth do Outlook Calendar no Harvest, a URL de callback redirecionava novamente para o destino do valor
state, causando um redirecionamento aberto; combinado com o fluxo de concessão implícita, isso podia expor o token a uma URL externa - O problema não estava na Microsoft, mas sim na integração do Harvest, onde o
redirect_uriOAuth registrado acabava funcionando como um intermediário de redirecionamento que enviava o usuário para um domínio definido pelo atacante - O
stateé um JSON codificado em URI, e ao inserir um domínio externo com barra emsubdomain, comoexample.com/, o redirecionamento passava a ir para algo comoexample.com/.harvestapp.com/... - A PoC mostra um fluxo em que a URL de autorização OAuth da Microsoft usa
client_id=0dcef4db-36d8-4aed-9cc5-ab43e1814884,response_type=id_token,response_mode=fragmentescope=openid, fazendo com que o fragmento#id_token=...seja anexado ao destino do redirecionamento - A vulnerabilidade foi reportada em 23 de agosto de 2020, teve o patch confirmado em 1º de agosto de 2023, e em 22 de outubro de 2023 a Harvest pediu desculpas dizendo que o atraso foi um human error
Como o token vazava no callback OAuth
- O Harvest é um software de rastreamento de tempo que permite ao usuário conectar o Outlook Calendar via OAuth
- Quando a autorização era bem-sucedida, o usuário era redirecionado para
https://outlook-integration.harvestapp.com/auth/outlook-calendar/… - Esse callback então redirecionava o usuário novamente para a URL fornecida dentro de
state, e nesse processo ocorria o redirecionamento aberto - O valor
stateobservado originalmente na URL de callback era um JSON codificado em URI- Ao decodificar, fica no formato
{"return_to":"/","subdomain":"hackerone295"} - O valor de
subdomainé usado para inferir um espaço do app Harvest, comohackerone295.harvestapp.com
- Ao decodificar, fica no formato
- Se fosse adicionada uma barra a
subdomain, como emexample.com/, a URL de callback passava a redirecionar para um domínio externo - Como essa URL de callback era o redirect_uri registrado no aplicativo OAuth, combinar o redirecionamento aberto com o fluxo de concessão implícita podia expor o token ao destino do redirecionamento
- A URL de autorização usada na PoC tinha os seguintes valores
client_id=0dcef4db-36d8-4aed-9cc5-ab43e1814884response_type=id_tokenredirect_uri=https://outlook-integration.harvestapp.com/auth/outlook-calendar/…?...scope=openidresponse_mode=fragmentstate=1nonce=123456
- No fim, o usuário era redirecionado para algo no seguinte formato
https://example.com/.harvestapp.com/auth/… access token]&state=1
- Não se tratava de uma vulnerabilidade da Microsoft
Tempo entre o reporte e o patch
- A equipe do Harvest respondeu muito pouco durante o processo de divulgação e correção da vulnerabilidade, e mesmo após reconhecê-la na triagem, a correção demorou bastante
- A vulnerabilidade foi reportada em 23 de agosto de 2020, e o Harvest fez o primeiro contato em 16 de outubro de 2020
- Em 27 de novembro de 2020, o relatório passou ao estado triaged
- Em 28 de abril de 2022, a empresa disse que estava corrigindo o problema e estimou 2 semanas, mas a correção real levou cerca de mais 1 ano
- Em 1º de agosto de 2023, foi confirmado que a vulnerabilidade havia sido corrigida
- Em 21 de outubro de 2023, o relatório foi tornado público por meio de uma postagem
- Na mesma data, o relatório ainda estava em estado de triagem, e embora a política de bug bounty mencionasse recompensa, nenhum bounty nem pontos no HackerOne foram pagos
- Em 22 de outubro de 2023, depois que a postagem ganhou atenção, a Harvest explicou e pediu desculpas dizendo que foi um human error
1 comentários
Opiniões no Hacker News
Como responsável pelo programa de bug bounty, vou explicar o que aconteceu internamente. Já pedi desculpas a @0xcrypto e expliquei internamente, mas acho correto resumir aqui também.
Desde o início, não consegui reproduzir completamente o problema e também não podia encerrá-lo por medo de estar deixando passar algo. Logo depois da minha última resposta dizendo que “encontraria uma solução”, eu estava me aproximando da conclusão de que não era reproduzível e, com a chegada de uma denúncia relacionada a OAuth parecida, houve confusão interna e achei, por engano, que já havíamos tratado e comunicado o caso.
Por causa das configurações de notificação, perdi as mensagens de acompanhamento, e a issue ficou indefinidamente no estado Triage, sem atualizações. Isso não é uma desculpa; como alguém que atuou por muito tempo como bug bounty hunter, sei muito bem como é frustrante esperar atualizações de uma empresa. A segurança dos clientes é prioridade máxima, e o que está escrito na página de segurança é verdade.
Além disso, agora também não está claro como será tratada uma denúncia que não pode mais ser reproduzida. Eu queria discutir mais na HackerOne, mas depois da mensagem de desculpas parece que voltou a haver falta de resposta.
O estado do app frequentemente diverge do estado do servidor; mudanças no servidor só aparecem depois de uma atualização forçada, ou mudanças feitas no cliente são revertidas após salvar. A experiência de rastreamento de tempo também é inconveniente: às vezes é preciso rolar para ver o botão, ou a posição dos botões de iniciar/parar/reiniciar/excluir muda constantemente conforme o estado do item. O app antigo não era bonito, mas funcionava sem problemas.
É bem preocupante. Quando usei o Harvest, o suporte era realmente excelente: respondia rápido, entendia em detalhes como os clientes usavam o produto e também explicava com cuidado maneiras criativas de aproveitar funcionalidades existentes.
Para recursos não implementados, a resposta era “vamos colocar no backlog, mas não podemos garantir”. Consta que há 30 pessoas na engenharia [1], mas não sei bem onde esse pessoal está sendo usado, porque também não vi outros recursos saindo rapidamente.
Comecei a receber spam como “usuário do Harvest” e suspeitei que tivessem vendido a lista de clientes; os responsáveis da empresa entraram em contato imediatamente, negaram enfaticamente e trataram o assunto com seriedade suficiente para investigar na hora. Isso foi bom.
Mas, no fim, isto também parece um problema de engenharia. Descobri uma forma bastante fácil de estimar clientes ativos, e isso também entrou no “backlog” e não foi corrigido por meses. A correção parecia muito trivial. Além disso, MFA só é oferecido ao fazer login com o Google [2]. Ainda assim, o app em si faz muito bem o que se propõe a fazer.
1: https://www.getharvest.com/about/meet-the-team
2: https://support.getharvest.com/hc/en-us/articles/36005266713...
A falha de comunicação com o autor da denúncia neste thread foi inteiramente minha, e, como expliquei na resposta no topo, vou garantir que isso não aconteça novamente.
Alguns dos pedidos de recursos acabaram entrando de fato no produto. Talvez não tenha sido por minha influência, mas pelo menos parecia que tínhamos ideias parecidas sobre um bom rastreador de tempo.
O título parece bastante injusto com a Microsoft. Dá a impressão de estar se aproveitando da atenção recente sobre incidentes de autenticação da Microsoft; assim que vi o título, pensei “mais uma invasão na MS?”.
Na prática, eram tokens da Harvest, e, por causa de uma vulnerabilidade de injeção no código da Harvest, apenas as permissões de acesso ao app Harvest foram expostas indevidamente. Teria sido igualmente vulnerável por trás de qualquer outro provedor de identidade.
Isso porque a vulnerabilidade afeta apenas a implementação OAuth para conexão com contas Microsoft. O título original era “Microsoft OAuth token leak via open redirect in Harvest App” e depois mudei para “Microsoft Account's OAuth tokens leaking via open redirect in Harvest App”. Ainda penso em alterá-lo e aceito sugestões.
A RFC 6749 aborda em detalhes como o servidor de autorização deve impedir esse tipo de ataque.
O servidor de autorização deve exigir obrigatoriamente o registro de URI de redirecionamento para clientes públicos, e é recomendável exigir também para clientes confidenciais. Quando uma URI de redirecionamento é fornecida na solicitação, o servidor de autorização deve validá-la comparando-a com o valor registrado.
Então fico curioso: o app Harvest não teria registrado um
redirect_urimalicioso; como isso foi possível? Será que o servidor OAuth da Microsoft ignora os parâmetros de URL dentro doredirect_uriao compará-lo com as URIs de redirecionamento registradas do cliente OAuth?state. Provavelmente a intenção era redirecionar para qualquer lugar.O fluxo pretendido seria: ir para a URL de autenticação do Harvest → redirecionar para a URL de autenticação da Microsoft com
redirect_uri=registered_uriestate=some_encoded_final_uri→ o usuário inserir as credenciais → redirecionar para a URI registrada → ler ostatee redirecionar novamente para a URI contida nele.Esse ataque ainda redireciona para uma URI permitida, mas esse endpoint lê o
statee repassa a resposta/o token sem alterações. Foram três erros: uso indevido destate, não criptografar nem validar ostatese ele fosse ser usado dessa forma, e ter ativado a concessão implícita. Se isso fosse necessário, deveriam ter criado um registro separado para um uso limitado.redirect_urido Harvest está registrado na Microsoft. Depois que o servidor OAuth da Microsoft redirecionou para o Harvest, o Harvest implementou seu próprio redirecionamento com base nos dados emstate.A concessão implícita é bem terrível por motivos como os mostrados neste texto.
Aliás, ela será removida da especificação OAuth 2.1 que está por vir: https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.htm...
Quer dizer que levou 3 anos para corrigir a vulnerabilidade? De agosto de 2020 a agosto de 2023; não sei o tamanho da equipe do Harvest, mas ainda assim parece absurdo.
Gostaria que algum especialista em OAuth explicasse esse problema com um pouco mais de detalhe. Li o blog algumas vezes, mas ainda não entendi a vulnerabilidade em si.
Pelo pouco que sei de OAuth, o app Harvest pede à Microsoft para verificar o usuário, a Microsoft verifica o usuário e, se der certo, redireciona para a URL de callback passando o token de acesso no corpo da resposta, não é assim?
Nesse caso, o autor do blog não apenas criou uma URL artesanal que fazia a URL de retorno ir para example.com em vez da URL de retorno real?
harvestapp, e a parte controlada pelo atacante fica dentro destate, que é praticamente opaco do ponto de vista do servidor OAuth.Com essa URL, é possível enviar a alguém um link de
login.microsoftonline.com, mostrar um prompt de login dizendo “entrar no Harvest” e então o atacante obter permissões relacionadas à conta real do Harvest.Normalmente isso seria impossível. O atacante até poderia registrar um novo app que redireciona para
example.com, mas isso seria inútil, porque ele não obteria um token de acesso com permissões relacionadas ao Harvest.O app OAuth do lado da Microsoft tem uma lista de redirecionamentos válidos permitidos, então uma tentativa como
login.microsoftonline.com/authorize?client_id=$harvestAppID&redirect_uri=attacker.comgera erro do lado da Microsoft. O ataque é possível porque uma URL válida deoutlook-integration.harvestapp.comrecebe o token de acesso e depois redireciona novamente para o site do atacante, levando também o token de acesso.outlook-integration.harvestapp.com. Depois que o callback OAuth2 tinha sucesso, eles usavam comostateum objeto JSON com instruções do que executar.A propriedade
subdomainera usada para redirecionar o navegador para um subdomínio deharvestapp.compassando#id-token. O problema é que o valor desubdomainera injetado diretamente na URL abaixo:https://${subdomain}.harvestapp.com/...#id-token=...
Se, no payload JSON,
subdomainfosse definido como um valor com barra no fim, comoattacker-controlled.com/, a URL virariahttps://attacker-controlled.com/.harvestapp.com/...#id-token=.., e o navegador iria para outro domínio, vazando o token.Portanto, não é um problema implícito do OAuth em si, e sim uma implementação ruim.
Ao configurar o workflow, em vez de criar uma lista real de URLs de callback confiáveis, é possível que tenham usado um curinga na lista de callbacks permitidos. Posso estar totalmente errado.
Não entendo por que esperaram 3 anos. Um período de carência antes da divulgação de 90 dias é suficiente
Ainda assim, essa prorrogação do prazo deve ser algo permitido pelo pesquisador ou por seu advogado/representante. É o tipo de coisa que uma empresa pode pedir de boa-fé em casos maiores que o normal
Se a HackerOne deixa as empresas engavetarem esse tipo de coisa por 3 anos, parece que não está fazendo seu papel direito
No nível mais básico, ela apenas fornece uma plataforma de divulgação de vulnerabilidades e um texto jurídico padrão para evitar que o pesquisador seja processado pelo departamento jurídico
Na maioria dos casos, as empresas rejeitam pedidos de divulgação. Se o pesquisador divulgar sem permissão, viola os acordos com a HackerOne e com a empresa e fica exposto a responsabilidade legal. Neste caso, parece que a empresa concordou com a divulgação e, mesmo que a resposta tenha sido muito lenta, acho que isso merece reconhecimento
Pessoalmente, também já tive várias vezes bugs com recompensas na casa dos quatro dígitos que ficaram mais de um ano sem correção, mas não achei que fosse culpa da HackerOne
A HackerOne não se importou. Mesmo após avisarmos várias vezes que aquela pessoa estava violando as regras deles, disseram que não podiam fazer nada
A impressão era de uma empresa que tinha sido reduzida a uma equipe mínima depois de já estar operacional, com atendentes de suporte sem autoridade respondendo perguntas. Isso foi há bastante tempo, então talvez hoje seja diferente, mas essa foi a impressão na época
Se as empresas sentirem que a HackerOne está passando dos limites e decidindo o que vai “permitir” que a empresa faça, elas simplesmente deixarão a HackerOne e criarão uma solução interna
Não entendo por que esse problema não foi encaminhado à Microsoft. A Microsoft poderia ter revogado o acesso desse aplicativo OAuth até que o problema fosse corrigido
Ainda assim, imagino que existam milhares de implementações igualmente ruins conectadas à Microsoft via OAuth