Atualização da Análise de Invulnerabilidade do Koa.js

Nov 26 2022
Não faz muito tempo, escrevi sobre uma vulnerabilidade que afetou o Koa.js em 2018.

Não faz muito tempo, escrevi sobre uma vulnerabilidade que afetou o Koa.js em 2018. Fiquei frustrado por ter que lidar com um problema de 2018 enquanto a data era 2022 e estava usando a versão mais recente do Koa. 4 anos se passaram. Isso é muito tempo. Certamente o problema não existe mais!? E foi e não foi ao mesmo tempo. Isso significa que eu estava errado e certo ao mesmo tempo. Então, aqui está uma atualização do post anterior para dizer como:

  • Acusei injustamente Sonatype e Dependency Track de não operar com informações de versão em casos específicos
  • Posso estar certo e errado ao mesmo tempo sobre uma vulnerabilidade

Índice OSS tem informações de versão

Se você leu meu post anterior, provavelmente se lembra que concluí que o problema não me afetou. A boa notícia é que eu estava certo. A má notícia é que acusei injustamente a Sonatype de não ter os números de versão nos dados fornecidos pelo OSS Index for Dependency Track para trabalhar. Acontece que eles têm as informações. Simplesmente não estava visível onde eu esperava que deveria estar visível. Veja a captura de tela abaixo tirada em 22/11/2022.

Faltam informações sobre as versões afetadas

É bem provável que o Dependency Track (DT) não funcione precisamente com o que é exibido na página acima, mas não me preocupei em verificar como eram os dados que o DT busca no índice OSS. A DT acabou de mostrar um link para o Índice OSS, onde pude aprender mais. Eu cliquei e caí nesta página.
Foi trazido à minha atenção pela Sonatype hoje que os números de versão estão disponíveis; Eu tenho que procurar em outro lugar. De fato, se você fizer login em sua conta e pesquisar Koa ou clicar no botão “Ver detalhes do koa” na captura de tela acima, poderá encontrá-lo. Veja a imagem abaixo.

Koa por versão e contador básico de vulnerabilidade

Então eu estava errado. Sonatype tem as informações no banco de dados. O problema está na camada de apresentação, então. Seria melhor se eles tivessem as versões afetadas listadas na página onde a vulnerabilidade é realmente discutida para que possamos ver e verificar se necessário.

Acusei injustamente o Sonatype OSS Index de não ter as informações da versão. Também acusei injustamente o Dependency Track de estar disposto a relatar puramente com base no nome da dependência se as informações da versão não estivessem disponíveis. (Ainda preciso descobrir se o último é verdadeiro ou falso, não verifiquei.)

Não desista ainda! Coisas mais interessantes para discutir são a vulnerabilidade real e as (espero) mudanças futuras no relatório de vulnerabilidade no Índice OSS. Vamos começar com a vulnerabilidade.

Script entre sites

Sonatype atribuiu XSS a Koa com base no processo de pensamento abaixo.

Koa é a entidade que está escrevendo a URL para a resposta HTML, não o desenvolvedor usando Koa

É definitivamente razoável esperar que o desenvolvedor valide o URL fornecido, provavelmente em uma lista de permissões, para evitar um redirecionamento aberto e não Koa, pois Koa não teria ideia de quais URLs você gostaria de permitir ou não. No entanto, como desenvolvedor, não acho que deveria me preocupar em fazer a limpeza XSS para uma URL que estou passando para um método redirect ().

Koa parece se importar com o XSS neste contexto, pois eles já têm um código para evitá-lo, a entidade HTML que codifica a URL quando a escreve no HTML

Eu concordo até certo ponto. Não concordo com o seguinte, por exemplo.

Eu não acho que deveria me preocupar em fazer a limpeza XSS para uma URL que estou passando para um método redirect()

Se você passar uma URL válida e segura para um método de redirecionamento que você (o desenvolvedor) forneceu, não precisa se preocupar com XSS (obviamente). Se você passar total ou parcialmente dados fornecidos pelo usuário, isso é algo com o qual você sempre deve se preocupar, seja você um desenvolvedor ou um profissional de segurança. Ainda assim, não é exagero esperar que Koa ajude o desenvolvedor.

Deixe-me dar um exemplo excelente que, com sorte, ajuda a entender de onde venho. No exemplo abaixo, passo os dados fornecidos pelo usuário de volta ao navegador no corpo da resposta. Eu faço isso sem qualquer validação, sanitização ou codificação.

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
  ctx.body = ctx.query.payload;
});

app.listen(4444);

*   Trying 127.0.0.1:4444...
* Connected to 127.0.0.1 (127.0.0.1) port 4444 (#0)
> GET /?payload=<img%20src=x%20onerror=alert(1)> HTTP/1.1
> Host: 127.0.0.1:4444
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=utf-8
< Content-Length: 28
< Date: Wed, 23 Nov 2022 10:59:17 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host 127.0.0.1 left intact
<img src=x onerror=alert(1)>%

  • Pense no que e como eu passo para o navegador no corpo da resposta
  • Considere (e provavelmente melhor controlar explicitamente!) o valor do cabeçalho Content-Type para a resposta
  • Saiba que o Koa, por padrão, ajusta automaticamente o valor do cabeçalho Content-Type com base no valor passado para ctx.body. (Tente passar, por exemplo aaa, e veja como muda)

Considerando o ctx.bodyexemplo “XSS” acima, parece que estávamos culpando Koa por implementar medidas específicas para evitar um desastre ao usar arquivos ctx.redirect(). Citando Sonatype novamente:

Koa parece se importar com XSS neste contexto, pois eles já têm código lá.

Isso significa que se Koa não se preocupasse com o XSS neste contexto, da mesma forma que eles não se importavam (e não deveriam) se preocupar com isso quando se trata de ctx.body, ninguém o teria sinalizado como uma vulnerabilidade do XSS? Isso é muito engraçado. Notei algumas semelhanças com minha análise anterior sobre Formidável documentada aqui e aqui .

Tendo dito o que eu disse até agora, um XSS está lá… e não ao mesmo tempo. Como isso é possível? Simples! Está lá, mas você não pode explorá-lo, a menos que:

  1. A vítima usa um navegador da Web antigo E
  2. Há um redirecionamento aberto implementado pelo desenvolvedor usando Koa's ctx.redirect(), AND
  3. O desenvolvedor confia completamente em todos os dados fornecidos pelo usuário e os passa literalmente paractx.redirect()

A página do relatório Sonatype dizia, como você pode ver na captura de tela de hoje, “Abrir redirecionamento levando a Cross-Site Scripting”. Meu post anterior questionou a parte do “redirecionamento aberto”:

Em primeiro lugar, não houve redirecionamento aberto. Só é aberto se você abrir e a diferença entre os dois PoCs (o original e o meu) mostra isso claramente.

A Sonatype também concorda que a Koa não é responsável por impedir redirecionamentos abertos. A preocupação deles, a minha preocupação e a principal preocupação de todos os outros era o XSS. Envolver o redirecionamento aberto foi apenas confuso. Ao mesmo tempo, não era tecnicamente irracional, como veremos mais adiante. (Também afetará a pontuação de vulnerabilidade.)

Como mencionado anteriormente, a exploração bem-sucedida do comportamento de Koa requer um redirecionamento aberto. Mas:

  • Koa não é responsável por impedir redirecionamentos abertos (conforme concluído anteriormente)
  • Koa não será executado ctx.redirect()sem a autorização do desenvolvedor
  • Koa não força os desenvolvedores a usar redirecionamentos

Portanto, há outra camada de proteção: os casos de uso. Qual a probabilidade de um desenvolvedor não querer algum controle sobre para onde um navegador é redirecionado? Muito improvável. Isso significa que, muito provavelmente, os desenvolvedores passarão dados controlados pelo usuário para serem ctx.redirect()anexados a outro pedaço de string. Assim, algo semelhante aos exemplos mostrados abaixo é o mais provável de acontecer:

ctx.redirect(`http://website.example.org/search?q=${userInput}`);
ctx.redirect(`/search?topic=${userInput}`);

Pontuação de Vulnerabilidade

Deve haver três (3) condições atendidas para que o bug seja explorável, conforme discutido no final da seção anterior.

  • Qual versão de um navegador é usada depende do usuário final
  • O uso do método de redirecionamento e como ele é usado fica a critério do desenvolvedor

O que também merece destaque é que o uso do método de redirecionamento de forma insegura é o vetor de ataque. Um vetor de ataque é necessário para uma exploração bem-sucedida. Koa não fornece o vetor de ataque; o desenvolvedor faz.

Vamos correlacionar isso com a definição de “Vulnerabilidade de Software” fornecida pelo NIST:

https://csrc.nist.gov/glossary/term/software_vulnerability

A parte que gostaria de destacar é “poderia ser explorada”. Considere o Koa como uma biblioteca de software. Você pode explorá-lo sem criar primeiro o vetor de ataque necessário? Não. Então, do ponto de vista do Koa, uma biblioteca de software, nenhum vetor de ataque é apresentado; sem isso, a exploração é impossível. Se interpretássemos a definição estritamente (o que eu faço), não poderíamos chamar o comportamento de Koa de vulnerabilidade.
Vamos tentar calcular uma pontuação CVSS sem um vetor de ataque:

Calculando pontuação CVSS sem vetor de ataque

Não há pontuação se não houver vetor de ataque. Eu diria que isso é consistente com a definição de vulnerabilidade de software fornecida pelo NIST.
Vamos experimentar e criar um cenário imaginário onde exista um vetor de ataque.

vetor de ataque fictício

Ainda existem vários problemas aqui, além do vetor de ataque imaginário. A Complexidade do ataque foi definida como Alta porque a exploração bem-sucedida requer um navegador antigo. Um invasor deve convencer as vítimas a baixar e instalar um navegador antigo.
Nesse sentido, a interação com o usuário é necessária, embora as métricas básicas de interação com o usuário não sejam exatamente sobre isso. Nesse cenário, se a exploração do XSS requer interação do usuário depende de como o aplicativo da Web usado redireciona e não do Koa. Também vale a pena mencionar que o JavaScript injetado não seria executado por conta própria, pois exigia interação do usuário em primeiro lugar: clicar no link na resposta HTML.

Então, o que podemos fazer para forçar uma pontuação de vulnerabilidade nisso? Devemos selecionar algo. Pensamento positivo, assumindo o pior, o que você preferir. Uma coisa é certa, você está fazendo um cálculo que não deveria fazer em primeiro lugar, e o resultado está tão longe da realidade que nem consigo encontrar as palavras para isso.

A próxima grande novidade são as métricas de impacto. Você deve saber o que o aplicativo da Web está prestes a contar sobre o impacto. Mas não estamos calculando uma pontuação de vulnerabilidade para um aplicativo da Web… Dado o contexto, esse XSS tem impacto zero no Koa. A Koa não manipula ou processa informações confidenciais por conta própria. O bug não tem impacto na disponibilidade ou integridade. Isso nos deixa com a pontuação final de 0,0, mesmo depois de forçar um vetor de ataque no cálculo.

Então a questão é, nós relatamos fatos ou ficção?

A Sonatype chamou minha atenção para a orientação oficial para Scoring Vulnerabilities in Software Libraries , lançada em 2019 com o CVSSv3.1. Confesso que não sabia disso, o que é bom e ruim. Bom porque teria resultado em um post retórico, ruim porque talvez se eu tivesse visto antes, teria perdido toda a minha esperança antes de me esforçar tanto para tentar explicar que não é o caminho certo para fazer isso. Então, o que diz a orientação oficial?

Ao pontuar o impacto de uma vulnerabilidade em uma biblioteca... O analista deve pontuar para o pior cenário razoável de implementação. Quando possível, as informações do CVSS devem detalhar essas suposições.

Portanto, a resposta é que a pontuação de vulnerabilidade é baseada em ficção.

Além disso, o que é um “ cenário de implementação de pior caso razoável ”? Se analisarmos muitos projetos que usaram o Koa e descobrimos que a maioria dos desenvolvedores implementou redirecionamentos abertos usando o ctx.redirect()método do Koa, pode ser razoável supor o pior. Fiz uma pesquisa rápida de código em projetos JavaScript ctx.redirect(no GitHub. Eu tenho 16.827 resultados de código. Ao pesquisar context.redirect(, recebi 15.751 resultados de código. Isso seria 32.578 resultados de código para analisar. Alguns deles usarão Koa, alguns Express e alguns podem ser outra coisa. (Claro, o contexto pode ser chamado por qualquer nome, não apenas ctxou context, portanto, pode haver ainda mais código para examinar.)

A questão é: passar dados fornecidos pelo usuário sem pensar para redirecionar é tão comum? Adotei uma abordagem semiautomática de análise, escrevendo um pequeno script para verificar todos os projetos que correspondiam aos critérios de pesquisa. Infelizmente, não consegui passar dos primeiros 1.000 acessos, pois o GitHub rejeitou outras solicitações:

{
    "message":"Only the first 1000 search results are available",
    "documentation_url":"https://docs.github.com/v3/search/"
}

Com base no que vi e considerando o que é discutido na próxima seção (“O Antigo Navegador da Web”), não acredito que seja razoável assumir uma implementação de pior caso. Não neste caso específico.

A orientação oficial também diz que:

Ao pontuar uma vulnerabilidade em uma determinada implementação usando a biblioteca afetada, a pontuação deve ser recalculada para essa implementação específica.

Isso é razoável e atenua o aspecto de ficção científica da situação até certo ponto. É assim que esse problema do Koa, com pontuação de vulnerabilidade de 9,8, acabou sendo 0,0 no nosso caso.

O bom é que a Sonatype concordou que a pontuação de vulnerabilidade de 9,8 não era razoável e eles estão dispostos a reduzi-la. Eu aprecio isso, e provavelmente muitos outros também.

Além disso, a Sonatype me disse que, quando adicionaram o problema ao sistema, acreditavam que seria melhor se seus clientes soubessem dessa situação potencialmente inesperada. Eles disseram, “era melhor alertar do que ignorar o que vimos e potencialmente ter um desfecho negativo”. E, claro, eles estavam certos.

Nunca questionei se questões de segurança deveriam ser comunicadas ou não. Sim, os problemas de segurança devem ser visíveis. O que me preocupa é como esses problemas são comunicados:

  • É apropriado chamar algo de vulnerabilidade ou mais apropriado chamá-lo de fraqueza de segurança ou comportamento padrão inseguro e assim por diante?
  • A pontuação de vulnerabilidade atribuída é razoável?
  • Detalhes e contexto suficientes são fornecidos?

Agora vamos falar um pouco sobre os antigos navegadores da web.

O navegador da Web antigo

Sem o bom pessoal da Sonatype, eu não teria pensado em navegadores antigos, provavelmente nunca mais.

Continuarei não considerando navegadores antigos no futuro também. Em primeiro lugar, há muitas coisas a serem lembradas; Não posso me preocupar com navegadores antigos. Em segundo lugar, quantos anos tem ( não clique no link se você não tiver senso de humor! ) um navegador da Web antigo? O que “velho” realmente significa? Se alguém usa apenas um navegador de aproximadamente 1 ano, é bem provável que já tenha problemas muito maiores do que o XSS.

Ainda assim, eu fiz algumas pesquisas e descobri que:

  • O Google Chrome 48.0.2564.109 (64 bits) de 2016 (!) nem exibia o corpo da resposta. Como o problema de Koa foi encontrado em 2018, pensei que voltar até 2016 deveria ser suficiente, mas descobri que não.
  • O Firefox 4.0 de 2011 exibia o corpo da resposta, mas exigia que os usuários clicassem no link para que a carga do JavaScript fosse executada. (Claro, é um link!)
  • O Firefox 52.0 de 2017, 1 ano antes do problema do Koa XSS ser relatado, já não exibia o corpo da resposta com a carga útil do JavaScript. O Firefox acabou de lançar um erro dizendo “Erro de conteúdo corrompido” devido a uma “violação do protocolo de rede”.

O Koa 0.0.1 foi lançado em 2013, então houve alguns anos em que o problema poderia ter sido explorado. A partir disso, provavelmente seria aceitável sinalizar esse problema (ainda não com uma pontuação de 9,8) até Koa 2.5.0 em 2018. Depois disso, porém, nada justifica nada acima de 1,0.

Enquanto eu estava cavando, encontrei algo interessante na Wikipedia . Deixe-me citar:

O Firefox 15 foi lançado em 28 de agosto de 2012 … O Firefox 15 introduziu atualizações silenciosas, uma atualização automática que atualizará o Firefox para a versão mais recente sem notificar o usuário, [65] um recurso que os navegadores Google Chrome e Internet Explorer 8 e superior têm já implementado

Portanto, todos os principais navegadores da web tinham um recurso de atualização automática antes do lançamento da primeira versão do Koa, o que significa que é provável que a maioria dos usuários estivesse usando um navegador da web atualizado. Vejamos o estado na época do “big bang”: 2013.

  • Firefox 15 : retornou um erro dizendo “Erro de conteúdo corrompido”, o que significa que o corpo da resposta não foi exibido para o usuário.
  • Chrome 24.0.1312 (WebKit 537.17) : não exibiu nenhum corpo de resposta. Ao olhar para a guia de rede das ferramentas do desenvolvedor, quase não havia nada visível, então tive que executar o Wireshark para ver se o navegador executou a solicitação em primeiro lugar. No Wireshark, ficou visível que o Chrome entrou em contato com meu serviço PoC e recebeu a resposta com a carga útil do JavaScript. Ele não renderizou o corpo da resposta. Melhor ainda, nada aconteceu.
  • Internet Explorer 11 (11.0.9600.19180) : Ele buscou a resposta do meu serviço PoC, que verifiquei usando o Wireshark. Não mostrava o corpo da resposta ao usuário. Ele retornou com a clássica página de erro integrada que diz: “Esta página não pode ser exibida”.

Tendo feito a pesquisa acima, vamos voltar a uma citação da Sonatype por um segundo:

Koa parece se importar com o XSS neste contexto, pois eles já têm um código para evitá-lo, a entidade HTML que codifica a URL quando a escreve no HTML

Embora esta afirmação esteja correta, o fato de nenhum dos principais navegadores permitir a exploração na época em que a primeira versão do Koa foi lançada, a citação sugere algo interessante, algo diferente do que a frase gostaria de sugerir. Observe que estou prestes a escrever especulações, pois não posso saber exatamente como os desenvolvedores do Koa estavam pensando. Posso facilmente imaginar que o(s) desenvolvedor(es) testaram o comportamento em questão usando um navegador da Web atualizado. Eles podem ter descoberto que a codificação HTML era adequada porque já era impossível obter o código JavaScript injetado executado a partir da hrefpropriedade doamarcação. Isso levanta uma questão séria. Tendo passado os últimos cinco anos mais no lado do desenvolvimento de software, isso também se aplica a mim: como desenvolvedor, se estou prestes a criar uma nova tecnologia da Web para o back-end, devo me preocupar com os navegadores da Web antigos? E se eu deveria, qual é a recomendação oficial da comunidade de segurança sobre quanto tempo atrás devo considerar navegadores da web antigos? Não deveríamos considerar que a melhor prática de segurança para usuários finais é manter seus navegadores atualizados, e o recurso de atualização automática também existe para ajudar nisso? Além disso, se esperamos que os desenvolvedores tenham em mente e testem com navegadores antigos, não podemos esperar que os profissionais de segurança façam o mesmo e nomeiem todos os navegadores da Web e suas versões que contribuem para um problema específico, em vez de apenas se referir a “navegadores antigos ”? Seria justo.

Uma coisa é certa, não há nada com que se preocupar desde anos…

O navegador moderno

Em minha análise, usando meu navegador da Web, verifiquei o corpo da resposta e o encontrei vazio. Não verifiquei se a resposta enviada pelo fio tinha um corpo. Acontece que foi apenas o Google Chrome que ignorou completamente o corpo da resposta; portanto, não apareceu. Isso foi bom o suficiente para mim. Razoavelmente, devo acrescentar.

Desde então, observei a resposta do meu serviço da Web de teste usando a versão mais recente do Koa. Podemos ver abaixo que havia um corpo de resposta HTML com o código JavaScript injetado lá:

*   Trying 127.0.0.1:4444...
* Connected to 127.0.0.1 (127.0.0.1) port 4444 (#0)
> GET /?payload=javascript:alert(1); HTTP/1.1
> Host: 127.0.0.1:4444
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Location: javascript:alert(1);
< Content-Type: text/html; charset=utf-8
< Content-Length: 71
< Date: Tue, 22 Nov 2022 17:55:12 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host 127.0.0.1 left intact
Redirecting to <a href="javascript:alert(1);">javascript:alert(1);</a>.

*   Trying 127.0.0.1:4444...
* Connected to 127.0.0.1 (127.0.0.1) port 4444 (#0)
> GET /?payload=%22><%2f HTTP/1.1
> Host: 127.0.0.1:4444
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Location: %22%3E%3C/
< Content-Type: text/html; charset=utf-8
< Content-Length: 61
< Date: Tue, 22 Nov 2022 22:18:49 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< 
* Connection #0 to host 127.0.0.1 left intact
Redirecting to <a href="&quot;&gt;&lt;/">&quot;&gt;&lt;/</a>.

Próximas mudanças

Abaixo está a lista de atualizações que a Sonatype está planejando implementar em relação a este problema:

  • Atualização da linha Resumo/Título para eliminar mal-entendidos (já aconteceu)
  • Reduzindo a pontuação de vulnerabilidade para 4,7 (já aconteceu)
  • Mencione que a vulnerabilidade só pode ser explorada se um usuário estiver executando um navegador mais antigo

Alterações adicionais que gostaria de ver

Além das mudanças mencionadas na seção anterior, algumas coisas podem ser melhoradas ainda mais. Esses são:

  • Reduzindo ainda mais a pontuação de vulnerabilidade, especialmente para as versões Koa lançadas após 2018.
  • Incluindo o número da versão de pelo menos os principais navegadores da web e suas datas de lançamento incluídas no relatório ao se referir aos “navegadores mais antigos”. Isso ajudaria significativamente qualquer pessoa ao “pontuar uma vulnerabilidade em uma determinada implementação usando a biblioteca afetada”. Por exemplo, se eu visse que um usuário precisava usar um navegador a partir de 2011, eu teria marcado o problema como “não afetado” no SCA em um segundo. É muito tempo economizado.
  • As versões Koa afetadas devem ser listadas na página da vulnerabilidade .

A propósito, a Sonatype também mencionou que possui algumas verificações interessantes em sua oferta comercial que, por exemplo, realizam verificações reais no nível do código para ver se um aplicativo foi afetado pelas vulnerabilidades relatadas. Isso parece legal e, dada a natureza de ficção científica de como as pontuações de vulnerabilidade são calculadas para bibliotecas, esses tipos de verificações são essenciais para reduzir a carga nas equipes de engenharia, especialmente se as coisas continuarem assim.

Conclusão

Isso é praticamente todas as atualizações que tenho. Estou feliz por ter procurado a Sonatype porque eles são muito profissionais e amigáveis. Foi um prazer trabalhar com eles nisso.

Em relação ao XSS em Koa: é algo que nenhum de nós deveria estar preocupado há vários anos.