Por Eduardo Muller

Como já trazido por Gabi e Vinícius, este é mais um resultado de pesquisa desenvolvida durante o programa de estágio da Tempest. Para quem ainda não sabe, durante esse programa, passamos por módulos e, ao final, devemos realizar um projeto de pesquisa sobre algum tema que consideramos interessante, e viável, para ser aprendido e apresentado em um mês. Tive bastante dificuldade em escolher o que raios eu gostaria de fazer como meu research. Tanta coisa massa pra aprender, um universo de possibilidades; somando-se a isso, um pouco de insegurança. Como eu, um estagiário com apenas 4 meses de experiência na área de segurança da informação, poderia aprender sobre algo em um mês para compartilhar com meus companheiros de trabalho, que já atuavam na área enquanto eu ainda assistia teletubbies e comia areia?

Depois de algumas noites mal dormidas, por pesadelos com este bendito research, muita procrastinação e troca de temas, cheguei à decisão final com a sugestão do meu querido diretor Fofão, que se tornou meu orientador nesta pesquisa, e é sobre isto que iremos “conversar” hoje.

Hoje em dia existem vários frameworks e bibliotecas que agilizam bastante o desenvolvimento de software, facilitando a codificação, tornando o trabalho mais rápido e criando camadas de abstração. Mas nem tudo são flores. Estaríamos abrindo mão de segurança por [facilidade|desempenho|conveniência]? Sob quais condições eu deveria incluir bibliotecas de terceiros, e de quais terceiros? Esta é uma discussão longa e complexa que está além do escopo deste blogpost. Quem quiser ler um pouco sobre esta reflexão, tem um blogpost do nosso head de engenharia de software Cheng aqui mesmo no SideChannel em que ele disserta um pouco estas bibliotecas e frameworks “mágicos”.

Mas então sobre o que é este blogpost? Este artigo é sobre uma breve avaliação realizada em um conjunto de bibliotecas que permitem a conversão de código HTML para PDF. Nosso objetivo aqui foi o de investigar quais tipos de vulnerabilidades podem ser inseridas num software através do uso de bibliotecas com a funcionalidade supracitada, e assim responder à pergunta: “dá pra hackear conversores de HTML para PDF?”. Bora ver…

O que avaliar?

Antes de sair feito doido fazendo fuzz e reportando os achados, precisei exercitar a paciência e determinar qual seria o escopo do meu research. O primeiro passo foi escolher quais bibliotecas eu iria testar. Acabei escolhendo 8 bibliotecas de acordo com os seguintes critérios: ser de código aberto, ser amplamente utilizada (pela quantidade de estrelas no Github e referências no StackOverflow) e limitar a escolha a apenas uma biblioteca por linguagem de programação. Com isso, chegamos na seguinte lista de bibliotecas:

Tabela 1 — bibliotecas escolhidas.

Ambiente de testes

Uma vez definidas as bibliotecas a serem avaliadas, precisei criar um ambiente de testes. Como visto, foram selecionadas 8 bibliotecas cada uma em uma linguagem de programação diferente. Algumas bibliotecas são bem simples e intuitivas (algumas possuem até uma interface command-line, bastante facilitadora) na realização do setup; enquanto outras são mais chatinhas. No final, foi mais trabalhoso que difícil montar o ambiente. Como a ideia era ter as bibliotecas sendo executadas num mesmo ambiente, utilizamos uma imagem padrão rodando um linux Xubuntu 18.04, exceto no caso da DinkToPDF, que foi executada em um Windows 10 dada a necessidade de ter o .Net framework.

Como avaliar?

Sabendo o que avaliar, e tendo o ambiente já configurado, o próximo passo foi definir como avaliar.

Fizemos então uma lista de perguntas (hipóteses) a serem respondidas contra cada uma das bibliotecas elencadas. As perguntas foram as seguintes:

#1A — A biblioteca permite a execução de código Javascript?

Avaliar se a biblioteca permite a execução de Javascript durante a conversão do HTML para PDF é possivelmente a pergunta mais interessante de todas as que realizamos neste trabalho. Isso porque o Javascript, diferente do HTML, é uma linguagem de programação, o que viabiliza realizar outras 6 perguntas: 1B, 1C, 1D, 1E, 3A e 4A.

Validação da hipótese

Para validar a hipótese de que a “biblioteca permite a execução de Javascript” nós utilizamos um código Javascript que, caso executado, altera o valor de uma div para “The javascript was executed” (string A). Caso a hipótese seja falsa, o valor da div será “The javascript was not executed” (string B). Com isso, bastou verificar o PDF gerado em busca da string A ou da string B.

Segue o código HTML/Javascript:

Code Snippet 1 — Hipótese #1A.

Resultado #1A

Dentre as 8 bibliotecas avaliadas, 5 permitem a execução de Javascript, segue o resumo:

Tabela 2 — resumo dos resultados de #1A.

#1B — Permite por padrão a execução de código Javascript?

Nestes casos, a permissão da execução de Javascript pode levar a alguns problemas de segurança, especialmente se os desenvolvedores não estiverem alertas a este comportamento. Todavia, caso a execução de Javascript não ocorra por padrão na biblioteca, o risco do desenvolvedor fazer uma bobagem diminui, daí a motivação para esta pergunta.

Dentre as 5 bibliotecas que permitem a execução de Javascript, todas o fazem por padrão. Assumir que tal comportamento é uma vulnerabilidade seria forçar um pouco a barra; mas, sem dúvida, este comportamento pode ser encarado como uma má prática de segurança.

#1C — É possível desabilitar a execução de código Javascript?

Como visto na pergunta #1B, todas as 5 bibliotecas que permitem execução de Javascript o fazem por padrão, mas quais delas permitem ao desenvolvedor desabilitar a execução de Javascript? Apenas a wkHTML. As outras 4 simplesmente não possuem a opção de desabilitar a execução de Javascript.

É possível realizar uma negação de serviço (DoS)?

Existem diversas formas de tentar fazer um DoS no servidor, mas a ideia que seguimos neste trabalho foi a de tentar “segurar” a execução do Javascript. Esta situação nos faz refletir o seguinte: a depender de como a biblioteca for utilizada, interromper essa execução viabilizaria uma negação de serviço? Bem, acaba sendo difícil afirmar para todas as situações; cabendo, assim, ao leitor realizar seus próprios testes em aplicações sob auditoria.

E, além disso, ainda surge um outro aspecto a ser considerado nesta tentativa de DoS: como poderíamos interromper essa execução? Para tanto, utilizamos aqui duas abordagens na tratativa de responder à questão:

#1D — verificar se é possível utilizar a função sleep() do Javascript, e;

#1E — verificar se é possível realizar um loop infinito.

O código Javascript utilizado para colocar o servidor “pra dormir” (função sleep) foi o seguinte:

Code Snippet 2 — DoS usando função sleep (#1D).

O código Javascript utilizado para colocar o servidor em loop foi o seguinte:

Code Snippet 3 — DoS usando loop infinito (#1E).

Portanto, foi possível verificar que:

  • O uso da função sleep() não funcionou em todas as bibliotecas;
  • O loop infinito funcionou em todas as bibliotecas que executam código Javascript.

Confesso que fiquei com algumas dúvidas em relação aos resultados desta hipótese, então pedi ajuda aos meus queridos colegas.

Basicamente, eu perguntei se eles já haviam conseguido realizar um DoS em algum projeto utilizando as ideias citadas acima. Fui informado pelos meus nobres colegas que raros são os projetos em que testamos DoS; e que eles nunca conseguiram fazer um DoS nesse cenário utilizando a função sleep. Entretanto, recebi relatos de que a utilização do loop infinito já funcionou em alguns casos, mas não na maioria. Não foi possível descobrir em quais bibliotecas conseguimos realizar o DoS em testes reais, o que é uma pena. Acredito ainda que o sucesso desse ataque depende de como a aplicação é arquitetada e codificada. Deixamos esse problema em aberto, como sugestão de pesquisa para quem quiser se aventurar na continuidade desta análise.

#2A — É possível realizar requisições do lado do servidor (SSRF)?

Como a biblioteca está, provavelmente, rodando em um servidor, será que é possível fazer com que este servidor realize requisições durante o processo de conversão do arquivo? Para corroborar esta hipótese, foi realizada a conversão de um arquivo contendo o código HTML a seguir:

Code Snippet 4 — Carregamento de conteúdo externo (#2A).

Ao analisar os resultados obtidos do processo de conversão do arquivo, o PDF gerado apresentou a página web em questão dentro do iframe, comprovando assim que esta biblioteca está vulnerável a ataques de Server Side Request Forgery — SSRF.

#3A — Há limite de requisições?

Mas, e se ainda houver o seguinte questionamento: a biblioteca limita a quantidade de requisições que podem ser feitas? Bem, isso poderia mitigar um ataque que utiliza as vulnerabilidades de SSRF e/ou DoS. Para verificar isto, foram feitas 200 requisições para um servidor em meu controle. Caso a biblioteca barrasse essas requisições, comprovaríamos que há um mecanismo que limita a quantidade de requisições realizadas. Segue o código utilizado:

Code Snippet 5 — Limite de Requisições (#3A).

Neste caso, todas as 200 requisições foram realizadas sem problemas, comprovando que a biblioteca não limita a quantidade de requisições realizadas.

É possível ler arquivos arbitrários?

Já que a biblioteca está rodando em um servidor, será que, durante o processo de conversão, poderíamos ler arquivos de forma arbitrária neste servidor?

Na tentativa de responder a este questionamento, utilizamos diferentes métodos para tentar ler o famigerado arquivo passwd de forma arbitrária.

#4A — No primeiro caso de teste, utilizamos o objeto XMLHttpRequest. A seguir, apresentamos o código utilizado:

Code Snippet 6 — Arbitrary File Read com XMLHttpRequest (#4A).

#4B — Para este caso, utilizamos a tag iframe.

Code Snippet 7 — Arbitrary File Read com iframe (#4B).

#4C — O teste a seguir faz uso da tag Object, conforme mostrado pelo código abaixo:

Code Snippet 8 — Arbitrary File Read comObject (#4C).

#4D — Por último, o código a seguir exibe o caso de teste utilizando a tag Portal:

Code Snippet 9 — Arbitrary File Read com Portal (#4D).

Como consequência dos testes realizados, ao se tentar acessar o arquivo passwd localizado na pasta etc, comprovamos que é possível ler arquivos de forma arbitrária, pois o PDF gerado continha as informações do arquivo supracitado.

Resultados

A seguir, apresentamos uma tabela exibindo os testes executados para cada biblioteca, e seu respectivos resultados: sendo true para as bibliotecas que tiveram resultado positivo no PDF gerado (ou seja, que estão vulneráveis); false para as que tiveram resultado negativo; e n/a caso o teste não se aplique.

Figura 10 — Tabela de Resultados

Das 8 bibliotecas testadas, Flying Saucer, WeasyPrint e DomPDF não permitiram a execução de código Javascript; e apenas a biblioteca wkHTML permitiu desabilitar o uso de Javascript.

As bibliotecas NODE-HTML-PDF, go-wkHTML, DinkToPDF, wkHTML e PDFKIT que permitiram o uso de Javascript, o fizeram por padrão. Ademais, permitiram sleep function, carregamento de conteúdo externo e acesso a arquivos via XMLHttpRequest; estando assim vulneráveis a ataques de Denial of Service (DoS), Server-side request forgery (SSRF) e Arbitrary File Read, respectivamente.

Apesar da Biblioteca DomPDF não ter sido vulnerável aos casos de teste aplicados nesta pesquisa, a mesma possui algumas CVE (Common Vulnerabilities and Exposures) conhecidas e relatadas em sua própria documentação. Vale ressaltar que, entre as bibliotecas testadas, esta foi a única que disponibilizou recomendações de segurança em sua documentação (https://github.com/dompdf/dompdf/wiki/Securing-dompdf).

“Massa! Bronca do caramba! E eu tô usando alguma dessas bibliotecas… e agora, como eu me protejo?”

Recomendações de Segurança

Como um dos objetivos da Tempest é contribuir para um mundo digital mais seguro, não poderíamos deixar de falar sobre como nos protegermos, não é mesmo?! Portanto, seguem algumas recomendações:

  • Realizar validação e filtração dos dados recebidos pelo usuário que serão convertidos de HTML para PDF;
  • Utilização de Sandbox para restringir o acesso e as permissões da biblioteca;
  • Caso não seja necessário, e a biblioteca permita, desabilitar a execução de Javascript;
  • Manter a biblioteca atualizada.

Bom, este foi meu projeto de pesquisa para o programa de estágio da Tempest. Espero que tenha contribuído de alguma forma para o seu conhecimento, assim como contribuiu para o meu. Gostaria de deixar meu agradecimento a todos que me ajudaram de alguma forma neste research, especialmente a Jodinho. Muito Obrigado!

Caso tenham alguma dúvida ou sugestão, podem mandar!

Referências

https://www.noob.ninja/2017/11/local-file-read-via-xss-in-dynamically.html

https://www.virtuesecurity.com/kb/wkhtmltopdf-file-inclusion-vulnerability-2/

https://securityonline.info/export-injection-new-server-side-vulnerability/

https://viralmaniar.github.io/web%20application%20testing/webapp%20security/HTML-to-PDF-Converter-Bugs/

https://www.darkmatter.ae/xen1thlabs/node-js-node-html-pdf-arbitrary-file-read-vulnerability-xl-19-026/

https://kongwenbin.com/write-up-for-gemini-inc-1/