Corrigindo a vulnerabilidade de XSS em `marked`
15 de maio de 2016
0 minutos de leituraHá algumas semanas, adicionamos ao nosso banco de dados uma vulnerabilidade de Cross-Site Scripting (XSS) no popular pacote marked. Esta publicação explica a vulnerabilidade, mostra como explorá-la em um aplicativo de exemplo e ensina como corrigir o problema no seu aplicativo.
marked analisa Markdown e o converte em HTML, facilitando a transformação de entradas renderizadas dos usuários — comentários, avaliações de produtos e solicitações de suporte — em textos com alguma formatação, incluindo links, negrito, itálico e muito mais. Como Markdown não oferece suporte a JavaScript, muitas vezes é considerado imune a Cross-Site Scripting e, portanto, seguro para renderizar entradas de usuários.
Na prática, porém, Markdown apenas reduz o risco de XSS — não o elimina por completo. Esta vulnerabilidade de XSS em marked, fácil de explorar, é um exemplo preocupante dessa diferença.
Como mostramos no nosso relatório State of Open Source Security 2019, as vulnerabilidades de XSS continuam aumentando. A maioria das vulnerabilidades divulgadas foi reportada no ecossistema PHP Packagist, seguida por npm e Maven Central.
A vulnerabilidade
Embora Markdown não ofereça suporte a scripts, marked (assim como outros clientes Markdown) oferece suporte a HTML inline. O HTML inline pode incluir tags, que podem ser usadas por invasores para injetar scripts maliciosos. Como marked é frequentemente usado para renderizar entradas de usuários na página, seus autores adicionaram uma opção de segurança para lidar com esse caso. O pacote oferece a opção sanitize, que detecta HTML e entradas perigosas e as codifica ou remove.
Embora sanitize venha (infelizmente) desativada por padrão, você pode ativá-la no seu aplicativo. Este exemplo mostra a opção sanitize em ação:
Detectar HTML é importante, mas a sanitização não termina aí. Embora Markdown não ofereça suporte a scripts, há suporte a links, o que abre a possibilidade de links javascript (por exemplo, javascript:alert(1)), que podem causar danos quando alguém clica neles. A funcionalidade sanitize leva isso em conta e remove links que se parecem com javascript:. Ela também remove links que usam a entidade HTML para dois-pontos, : (por exemplo, javascript&58;alert(1)). Infelizmente, mesmo levando isso em conta, há um caso que passa despercebido…
HTML é um formato bastante permissivo, e os navegadores são muito tolerantes ao processá-lo. Um exemplo dessa tolerância é que, ao processar entidades HTML, os navegadores não exigem os dois-pontos no final e aceitam tanto : quanto :. Já a sanitização em marked exige os dois-pontos e trata o texto como texto simples se eles não forem encontrados. Isso significa que : será removido, mas &58this; será simplesmente enviado para a saída. Um invasor pode usar essa técnica para contornar o marked e ainda fazer com que os navegadores executem um script.
Veja uma demonstração em código de onde a sanitização funciona — e onde não funciona:
O navegador interpreta : da mesma forma que :, executando o script quando alguém clica. É claro que o script incluído por nós não faz muita coisa, mas um invasor poderia injetar um payload muito mais sofisticado, violar a política de mesma origem do navegador e causar todos os danos que um XSS pode provocar.
Exploração ao vivo no Goof
Assim como fizemos ao falar da vulnerabilidade relacionada ao Buffer no mongoose, adicionamos esta vulnerabilidade ao nosso aplicativo vulnerável, Goof. Acreditamos que explorar uma vulnerabilidade e ver o código vulnerável ajuda a entender melhor o problema. Você pode clonar o Goof e executá-lo seguindo as instruções no GitHub.
Goof é um aplicativo de lista de tarefas e usa marked para oferecer suporte a Markdown nas notas. O Goof é um aplicativo de tarefas de primeira linha e, claro, um aplicativo assim PRECISA oferecer suporte a links, negrito e itálico!
Por exemplo, ao inserir os itens de tarefa Buy **beer** e [snyk](https://snyk.io/), o resultado será o negrito e o hiperlink esperados, como mostrado a seguir:

Agora, vamos tentar inserir um payload malicioso. A captura de tela a seguir mostra o estado visual e do DOM após a inserção de cada um dos três payloads de ataque acima. Como esta é uma lista de tarefas, o primeiro item inserido aparece por último (em terceiro lugar) na lista.
Como você pode ver, os dois itens inferiores, que mostram as duas primeiras tentativas de ataque, foram reduzidos pelo sanitizador a <p></p> e <p>)</p>. Já o payload no topo criou um hiperlink que executará javascript:this;alert(1). Executar this não faz nada (apenas referencia uma variável existente), enquanto o alerta exibe uma janela pop-up.
Depois de deixar o alerta da nossa exploração mais claro e clicar no link, vemos o seguinte:

Você pode reproduzir esse fluxo de ataque instalando o Goof localmente e executando os payloads de exploração no diretório exploits.
Como corrigir
De forma um tanto incomum, não existe uma versão oficial do marked que corrija o problema. O repositório do marked está inativo desde o verão passado, e a vulnerabilidade só foi divulgada posteriormente.
No entanto, você pode corrigir o problema facilmente aplicando um patch com o Wizard da Snyk. Esse patch foi criado pela nossa equipe de pesquisa em segurança e se baseia na solicitação de pull original de Matt Austin ao repositório.
Assim como todos os outros patches da Snyk, os arquivos detalhados do patch estão disponíveis no nosso banco de dados de vulnerabilidades de código aberto. Na verdade, há três patches diferentes para versões distintas do marked; o mais simples se resume a isto:

Atualização: os mantenedores do marked acabaram lançando uma nova versão do marked (v0.3.6) em 30 de julho de 2016, que corrige esse problema.
Outra opção é usar um pacote Markdown alternativo, como markdown-it ou remarkable. Não há garantia de que estejam livres de vulnerabilidades, mas, no momento, não há vulnerabilidades conhecidas sem correção.
Nova publicação, vulnerabilidade antiga
Um último aspecto interessante desta vulnerabilidade é que, na verdade, ela é bastante antiga. O problema foi reportado em maio de 2015, mas só foi registrado em bancos de dados de vulnerabilidades no mês passado. Atrasos assim não são tão raros: o volume de problemas no GitHub é enorme e difícil de acompanhar.
Se você encontrar um problema de segurança reportado em um pacote npm, tenha ele sido corrigido ou não, avise-nos pelo e-mail security@snyk.io. Vamos analisá-lo e adicioná-lo ao nosso banco de dados. O tamanho do ecossistema npm exige que todos trabalhemos juntos para nos mantermos informados sobre esses problemas e ajudarmos a manter tudo seguro.
Comece a participar de desafios de Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo sob demanda ao nosso workshop virtual introdutório.


