Someone left the server room door unlocked, the safe open, and taped the combination to the wall
CVE-2026-48907 is an unauthenticated remote code execution vulnerability in the JCE (Joomla Content Editor) extension for Joomla, affecting all versions from 1.0.0 through 2.9.99.4. It chains three independent design failures in the profile import workflow: (1) the profiles.import endpoint performs no ACL or authentication check — only CSRF validation, and the token is embedded in every public page; (2) the upload handler calls File::makeSafe() which strips filesystem-illegal characters but never validates file extensions, so shell.xml.php sails through; (3) the code explicitly passes $allow_unsafe = true to File::upload(), disabling Joomla's built-in extension blacklist. An attacker sends three HTTP requests — harvest the CSRF token, POST a PHP webshell to /index.php?option=com_jce&task=profiles.import, then GET the uploaded file from /tmp/ — and owns the web server. The file lands in a web-accessible directory with its .php extension intact. No credentials, no user interaction, no special network position required.
The vendor's CVSS 3.1 score of 9.8 (and CVSS v4 score of 10.0) is not inflated — if anything, the v3.1 label undersells the real-world urgency. This is a zero-friction, pre-auth RCE that has been weaponized into automated scanning campaigns within days of disclosure. CISA added it to the KEV catalog on 2026-06-16 with a 3-day remediation deadline for FCEB agencies. Multiple public PoCs exist, a nuclei template is published, and exploit-kit operators are deploying webshells, cryptominers, and botnet enrollment payloads at scale. The roughly 2.5 million active Joomla installations — many running JCE as the most popular editor extension — represent a massive, internet-facing attack surface with near-zero exploit complexity.
5 steps from start to impact.
Discover vulnerable Joomla+JCE target
com_jce component endpoint. Automated campaigns have been sweeping the internet since early June 2026.- Target runs Joomla with JCE extension ≤ 2.9.99.4
- Target is reachable over HTTP/HTTPS
- None — Joomla sites are inherently internet-facing, and JCE is one of the most popular Joomla extensions
CVE-2026-48907.yaml in projectdiscovery/nuclei-templates. Shodan/Censys queries for Joomla+JCE fingerprints.Harvest CSRF token from public page
- Target serves public pages (virtually all Joomla sites)
- None — the CSRF token is public by design in Joomla's architecture
Upload PHP webshell via profile import endpoint
/index.php?option=com_jce&task=profiles.import with the harvested CSRF token and a file named with a dual extension (e.g., shell.xml.php). The endpoint performs no authentication check (Weakness 1), File::makeSafe() does not strip the .php extension (Weakness 2), and File::upload() is called with $allow_unsafe = true (Weakness 3). The file is written to the web-accessible /tmp/ directory with its PHP extension intact. Weaponized tools: grayxploit PoC, 0xgh057r3c0n PoC, ywh-jfellus PoC, bayu06802 nuclei+Python toolkit.- JCE
profiles.importendpoint is accessible (default in all JCE installs) - Web server allows PHP execution in
/tmp/(default Apache/Joomla configuration)
- A WAF rule blocking multipart POSTs to
profiles.importwould stop this, but almost no one has that rule pre-configured - Server-level config disabling PHP in
/tmp/would break execution, but this is not a Joomla default
task=profiles.import with file upload payloads. IDS rules matching the specific URI pattern.Execute webshell for full server compromise
/tmp/shell.xml.php (or whatever filename they chose). Apache processes the file using the .php handler based on the final recognized extension. The webshell executes with the permissions of the web server process (typically www-data or apache). From here, the attacker can execute arbitrary commands, read database credentials from configuration.php, pivot to the database, exfiltrate data, or establish persistent backdoor access.- PHP execution is enabled for the
/tmp/directory - Web server process has sufficient OS-level permissions
- If the server runs PHP-FPM with
open_basedirrestrictions covering/tmp/, execution may fail — but this is uncommon in default Joomla hosting setups
.php files in /tmp/. Web server access logs showing requests to /tmp/*.php. YARA rules for common webshell patterns.Post-exploitation: lateral movement and persistence
configuration.php to obtain MySQL credentials, enumerates the local network, and attempts lateral movement via database pivots, SSH key harvesting, or exploiting internal services reachable from the DMZ. Observed payloads in the wild include cryptominers, botnet agents, defacement scripts, and additional webshells for persistence. Rogue JCE profiles (named with pattern J followed by six digits, e.g., J938560) are created for persistent re-entry.- Initial webshell execution succeeded
- Internal network is reachable from the web server
- Network segmentation between web DMZ and internal networks limits lateral movement
- EDR on the web server host can detect post-exploitation activity
J[0-9]{6} pattern. Monitor for new PHP files in web-accessible directories. Database audit logs for credential access.task=profiles.import on the com_jce component. This breaks the upload step (Step 3) of the attack chain entirely without affecting normal JCE editor functionality, since profile import is an admin-only operation that legitimate users access through the Joomla backend, not the frontend. Per the noisgate mitigation SLA for CRITICAL, deploy within 3 days. Cloudflare, Akamai, and Imperva have pre-built virtual patches available — enable them immediately..htaccess file (Apache) or location block (Nginx) to the Joomla /tmp/ directory that disables PHP execution (php_flag engine off for Apache, location ~* \.php$ { deny all; } for Nginx). This breaks Step 4 — even if the file is uploaded, it cannot execute. Deploy within 3 days per noisgate mitigation SLA for CRITICAL. This is a quick server-level change that does not require a Joomla extension update.J[0-9]{6} (e.g., J938560, J991471). Scan /tmp/ and other web-accessible directories for unexpected .php files. Review web server access logs for POST requests to task=profiles.import and GET requests to /tmp/*.php. Check configuration.php access patterns for credential harvesting. This does not reduce vulnerability but ensures you catch attackers who have already exploited the flaw. Execute immediately alongside patching.- Disabling Joomla user registration does not help — the vulnerability requires no Joomla account whatsoever. The exploit targets an unauthenticated endpoint, so registration settings are irrelevant.
- IP-based rate limiting is insufficient — the full exploit chain is only 3 HTTP requests, well under any reasonable rate limit threshold. A single request per minute is enough to compromise a target.
- Joomla core updates alone do not help — JCE is a third-party extension. Updating Joomla core without updating the JCE extension leaves the vulnerability fully exploitable.
- CSRF token rotation / regeneration does not help — the attacker harvests a fresh token on every attempt from any public page. Shortening token lifetime adds negligible friction.
The supporting signals.
| In-the-Wild Exploitation | Confirmed active exploitation. CISA added to KEV on 2026-06-16 citing active exploitation. Automated scanning campaigns observed sweeping ~2.5M Joomla sites. Payloads include PHP webshells, cryptominers, defacement scripts, and botnet enrollment. Rogue profiles with J[0-9]{6} naming pattern are a signature of the active exploit kit. |
|---|---|
| Proof-of-Concept Availability | Multiple public PoCs. GitHub repos: grayxploit/CVE-2026-48907 (3-request chain walkthrough), 0xgh057r3c0n/CVE-2026-48907, NoXiVaR/CVE-2026-48907, ywh-jfellus/CVE-2026-48907 (YesWeHack researcher), bayu06802/CVE-2026-48907 (Python + nuclei). Also indexed on Sploitus. |
| EPSS Score | User-supplied: 0.1619 (16.19%) at ~97th percentile. Nuclei template metadata reports 0.80425 (99.575th percentile) — likely a later snapshot reflecting KEV addition and mass exploitation. Either way, this is in the top 1% of all CVEs for predicted exploitation probability. |
| KEV Status | Listed. Added 2026-06-16. FCEB remediation deadline: 2026-06-19 (3-day window). CISA alert. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. CVSS:4.0 score = 10.0. Every base metric is at maximum severity: network-accessible, low complexity, no privileges, no user interaction, full CIA impact. The vector accurately reflects the real attack — zero friction from initial access to code execution. |
| Affected Versions | JCE (Joomla Content Editor) 1.0.0 through 2.9.99.4 — both Free and Pro editions. This encompasses the entire version history of the extension. |
| Fixed Versions | JCE 2.9.99.5 (released 2026-06-03) — core fix. JCE 2.9.99.6 (released 2026-06-08) — additional hardening. Current recommended version: ≥ 2.9.99.6. Vendor also shipped a standalone patch for sites unable to do a full extension upgrade. |
| Scanner / Detection Coverage | Nuclei template CVE-2026-48907.yaml available in projectdiscovery/nuclei-templates (verified, 3 requests, intrusive/modifying). Tenable plugin available. WAF vendors (Cloudflare, Akamai, Imperva) have deployed virtual patches. |
| Disclosure Date | 2026-06-05 (CVE published). Patch released 2026-06-03 (2 days before CVE publication). KEV added 2026-06-16. |
| Reporting Researcher / Org | Discovered via YesWeHack vulnerability research team. YesWeHack writeup published same day as advisory with full technical chain analysis. Individual researcher handle: ywh-jfellus (GitHub PoC author). |
Sources.
- CISA KEV Alert — CVE-2026-48907 Added
- GitHub Advisory GHSA-c3f5-4g7f-qjqj
- YesWeHack — Unauthenticated RCE in JCE Extension
- grayxploit/CVE-2026-48907 — 3-Weakness Chain PoC
- JCE 2.9.99.5 Security Update — Official Vendor Advisory
- The Hacker News — CISA Warns of Actively Exploited JCE Flaw
- Nuclei Template — CVE-2026-48907.yaml
- Tenable — CVE-2026-48907
Why this verdict
- Zero authentication friction: The entire chain requires no credentials whatsoever. The CSRF token — the only 'gate' — is embedded in every public page Joomla serves. This is not a gate; it is a welcome mat.
- Trivial weaponization: The exploit requires exactly 3 HTTP requests and is fully automated in multiple public PoCs, a nuclei template, and at least one active exploit kit. Complexity is effectively zero — a script kiddie can pop a shell.
- Massive, inherently exposed attack surface: Joomla is a web CMS — ~2.5 million active sites, and JCE is one of its most popular extensions. Unlike vulnerabilities requiring internal network access, every affected installation is reachable from the internet by design. There is no 'only 1% are exposed' argument to make here.
- Active mass exploitation confirmed: CISA KEV-listed with a 3-day remediation deadline. Automated campaigns deploying webshells, cryptominers, and botnet agents are confirmed. This is not theoretical — it is burning right now.
- Role multiplier: (a) *Low-value role:* Dev/staging Joomla sites — compromise is contained, blast radius is host-level. (b) *Typical role:* Production website or web application — compromise yields webshell, DB credentials, customer data, and a beachhead into the DMZ. (c) *High-value role:* Joomla instances serving e-commerce, customer portals, government services, or intranet applications — compromise leads to PII/payment data exfiltration, supply-chain risk if serving software downloads, and lateral movement into internal networks. The chain succeeds identically in all roles (no role-specific prerequisites). While Joomla is not a canonical high-value-role component (it is not a DC, IdP, or hypervisor), 100% of its installs are internet-facing by definition, and a web server compromise is the canonical initial access vector for deeper network penetration. The blast radius is host → tenant → potentially network, depending on segmentation.
- No downward adjustments applicable: Every friction audit checkpoint returns zero friction. Attacker position: unauthenticated remote. Prerequisites: none beyond network reachability. Population exposed: 100% of installs. Modern security tool that stops it: WAF with specific rule (not default). There is no compounding downward pressure on this vulnerability.
Why not higher?
A 9.8 is already the effective ceiling for CVSS v3.1 single-system impact. The CVSS v4 score of 10.0 captures the additional subsequent-system impact. noisgate does not inflate beyond the CVSS v3.1 vector-calculated score. The only reason this isn't a flat 10.0 is the Scope metric — exploitation grants web server process privileges, not necessarily root/SYSTEM, and subsequent system impact depends on network segmentation.
Why not lower?
Every possible friction point that could justify a downgrade is absent. There is no authentication requirement, no user interaction, no complex race condition, no unusual configuration prerequisite, and no limited attack surface. The vulnerability is being actively exploited at scale with automated tooling. KEV listing with a 3-day federal deadline confirms this is as urgent as it gets. Downgrading would be professionally irresponsible.
Crowdsourced verification payload.
Run this script on the Joomla web server host as any user with read access to the JCE extension files (typically root or www-data). Invoke with: sudo bash check_cve_2026_48907.sh /var/www/html where the argument is your Joomla installation root. No network access or external dependencies required.
#!/usr/bin/env bash
# check_cve_2026_48907.sh — Detect CVE-2026-48907 (JCE unauthenticated RCE)
# Usage: bash check_cve_2026_48907.sh /path/to/joomla
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -euo pipefail
JOOMLA_ROOT="${1:-/var/www/html}"
JCE_MANIFEST="${JOOMLA_ROOT}/administrator/components/com_jce/jce.xml"
JCE_PROFILES_CONTROLLER="${JOOMLA_ROOT}/administrator/components/com_jce/controller/profiles.php"
if [ ! -f "$JCE_MANIFEST" ]; then
echo "UNKNOWN — JCE extension not found at ${JOOMLA_ROOT}. Verify the Joomla root path."
exit 2
fi
# Extract JCE version from manifest
JCE_VERSION=$(grep -oP '<version>\K[^<]+' "$JCE_MANIFEST" 2>/dev/null || echo "")
if [ -z "$JCE_VERSION" ]; then
echo "UNKNOWN — Could not parse JCE version from ${JCE_MANIFEST}."
exit 2
fi
echo "Detected JCE version: ${JCE_VERSION}"
# Compare versions using sort -V
PATCHED_VERSION="2.9.99.5"
if printf '%s\n%s\n' "$PATCHED_VERSION" "$JCE_VERSION" | sort -V | head -n1 | grep -qx "$PATCHED_VERSION"; then
echo "PATCHED — JCE ${JCE_VERSION} is >= ${PATCHED_VERSION}. CVE-2026-48907 is fixed."
exit 0
else
echo "VULNERABLE — JCE ${JCE_VERSION} is < ${PATCHED_VERSION}. CVE-2026-48907 applies."
# Additional check: look for IOCs
echo ""
echo "--- IOC Hunt ---"
if find "${JOOMLA_ROOT}/tmp" -name '*.php' -type f 2>/dev/null | head -5 | grep -q .; then
echo "WARNING: PHP files found in ${JOOMLA_ROOT}/tmp/ — possible webshell:"
find "${JOOMLA_ROOT}/tmp" -name '*.php' -type f -ls 2>/dev/null | head -10
else
echo "No PHP files found in ${JOOMLA_ROOT}/tmp/ (good)."
fi
# Check for rogue JCE profiles in database config
echo ""
echo "Recommendation: Query the database for JCE profiles matching pattern J[0-9]{6}."
exit 1
fi