
Joomla 6.1.3 - Alta - Remota - Sobrescritura - sobrescritura de restos del gestor de medios
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-media-override
Clase: Privilegio residual Alcance: Remoto
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
CVE-2026-48947 enseñó a la API de medios del webservice a exigir core.edit cuando se establece override. La API JSON del sitio todavía no lo hace. El Author por defecto tiene core.create sobre com_media y no tiene core.edit. El dispatcher del sitio considera core.create suficiente y carga el ApiController del administrador. postFiles() entonces pasa override directamente a createFile().
Un Author estándar puede reemplazar un archivo que ya está en disco. Los visitantes siguen accediendo a la misma URL pública y reciben los nuevos bytes.
| ID | sin CVE todavía |
| Clase | Privilegio residual / integridad |
| Alcance | Remoto (Author por defecto, sin token de API) |
| CWE | CWE-863, CWE-639 |
| CVSS | Medio: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Producto | Joomla CMS |
| Afectado | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), API JSON com_media del sitio |
| Autenticación | Author por defecto (grupo 3). Sin token de API. |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Iniciar sesión como un Author estándar y reemplazar un archivo que ya está en el sitio. Esto es defacement / bait-and-switch de medios almacenados, no una toma de control del sitio. La lista de denegación de extensiones no cambia, así que esto no es una subida de .php.
| Privilegio | Método / ruta | Tipo | Probado en laboratorio o inferido |
|---|---|---|---|
| Sobrescribir un archivo de medios existente (logo, imagen de artículo) | Sitio POST /index.php?option=com_media&task=api.files&format=json&path=local-images:/ con JSON override: true | integridad | Probado en laboratorio. HTTP 200, SHA en disco cambiado, GET público /images/joomla_black.png sirvió el nuevo PNG que lleva JOOMLA-MEDIA-OVERRIDE-WITNESS |
| Defacear cualquier cosa que todavía apunte a esa URL | Misma ruta pública, sin cache-bust | integridad | Probado en laboratorio que los bytes públicos cambiaron. Inferido para otros adaptadores bajo images/ |
| Crear un nuevo archivo permitido | Mismo POST sin override | previsto | Author tiene core.create sobre com_media |
| Editar mediante PUT | PUT misma tarea, la ruta incluye el nombre del archivo | n/a | Probado en laboratorio cerrado. HTTP 403. putFiles() ya exige core.edit |
| Crear cuando el nombre existe, sin override | POST override: false (por defecto) | n/a | Probado en laboratorio cerrado. HTTP 409, SHA original sin cambios hasta el POST con override |
Subir .php u otros tipos denegados | lista de denegación de createFile / isMediaFile | n/a | Inferido cerrado. Este laboratorio solo envía un PNG |
| Llamar a la API de medios del webservice con override | /api/index.php/v1/media/files | n/a | Inferido cerrado. MediaController::allowAdd comprueba core.edit cuando se establece override (CVE-2026-48947) |
| Super User / RCE | n/a | n/a | Cerrado. El Author sigue siendo Author |
Fijé 6.1.3 después de la oleada de JSST de 2026-07/08. La API de grupos ya exige core.admin. La API de medios del webservice ya exige core.edit con override. El controlador interno todavía no lo hace.
El Author por defecto tiene core.create sobre com_media y nada en core.edit:
{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.delete":{"5":1}}
El Dispatcher del sitio deja pasar core.create y carga el ApiController del administrador. postFiles() entonces:
if (!$this->app->getIdentity()->authorise('core.create', 'com_media')) {
throw new \Exception(Text::_('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED'), 403);
}
$override = $content->get('override', false);
if ($mediaContent) {
$name = $this->getModel()->createFile($adapter, $name, $path, $mediaContent, $override);
Webservice corregido (api/.../MediaController.php):
if ($this->input->json->get('override', false)) {
return $user->authorise('core.edit', 'com_media');
}
Caminos equivocados ya registrados: tratar PUT como el residuo (PUT ya es 403); tratar la ruta del webservice como todavía abierta (CVE-2026-48947 la cerró); leer 409 sin override como un fallo (esa es la ruta prevista de crear-si-no-existe). Teatro: una reverse shell. El oráculo es POST override 200 más PUT 403 más GET público del PNG testigo.
Residuos del mismo pin: levels API, menu items API, articles catid.
python3 joomla-media-override-Abraxas-Labs.py
o
cd lab
./run.sh
Objetivo solo http://127.0.0.1:19540. Imagen joomla:6.1.3-php8.3-apache más mariadb:11.4.
SUCCESS joomla-media-override post-http=200 override=yes bytes-changed=yes put-http=403 no-edit=yes conflict-http=409 JOOMLA-MEDIA-OVERRIDE-WITNESS
La misma comprobación override -> core.edit que MediaController::allowAdd dentro de ApiController::postFiles() antes de createFile(). Mantener PUT sobre core.edit.
0f2d4de4729eb951e0878fec98c7990da07915b2)ApiController.php postFilesGNU Affero GPL v3.0. Ver LICENSE. Solo laboratorio en loopback. Sin garantía.