
Joomla 6.1.3 - Élevé - À distance - Écrasement - écrasement résiduel du gestionnaire de médias
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-media-override
Classe : Privilège résiduel Portée : Distante
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
CVE-2026-48947 a appris à l'API webservice media à exiger core.edit lorsque override est défini. L'API JSON du site, elle, ne le fait toujours pas. L'Author par défaut possède core.create sur com_media et pas core.edit. Le dispatcher du site considère core.create comme suffisant et charge l'ApiController de l'administrateur. postFiles() transmet ensuite override directement à createFile().
Un Author standard peut remplacer un fichier déjà présent sur le disque. Les visiteurs continuent d'accéder à la même URL publique et reçoivent les nouveaux octets.
| ID | pas encore de CVE |
| Classe | Privilège résiduel / intégrité |
| Portée | Distante (Author par défaut, sans jeton d'API) |
| CWE | CWE-863, CWE-639 |
| CVSS | Moyen : 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Produit | Joomla CMS |
| Affecté | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), API JSON com_media du site |
| Auth | Author par défaut (groupe 3). Sans jeton d'API. |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
Se connecter en tant qu'Author standard et remplacer un fichier déjà présent sur le site. Il s'agit de défiguration / d'échange de média stocké, pas d'une prise de contrôle du site. La liste de refus des extensions est inchangée, donc ce n'est pas un téléversement de .php.
| Privilège | Méthode / chemin | Type | Prouvé en lab ou déduit |
|---|---|---|---|
| Écraser un fichier média existant (logo, image d'article) | Site POST /index.php?option=com_media&task=api.files&format=json&path=local-images:/ avec JSON override: true | intégrité | Prouvé en lab. HTTP 200, SHA du disque modifié, GET public /images/joomla_black.png a servi le nouveau PNG contenant JOOMLA-MEDIA-OVERRIDE-WITNESS |
| Défigurer tout ce qui pointe encore vers cette URL | Même chemin public, sans cache-bust | intégrité | Prouvé en lab que les octets publics ont changé. Déduit pour les autres adaptateurs sous images/ |
| Créer un nouveau fichier autorisé | Même POST sans override | prévu | Author possède core.create sur com_media |
| Modifier via PUT | PUT même tâche, le chemin inclut le nom de fichier | n/a | Prouvé en lab fermé. HTTP 403. putFiles() exige déjà core.edit |
| Créer quand le nom existe, sans override | POST override: false (par défaut) | n/a | Prouvé en lab fermé. HTTP 409, SHA d'origine inchangé jusqu'au POST avec override |
Téléverser .php ou d'autres types refusés | Liste de refus createFile / isMediaFile | n/a | Déduit fermé. Ce lab n'envoie qu'un PNG |
| Appeler l'API webservice media avec override | /api/index.php/v1/media/files | n/a | Déduit fermé. MediaController::allowAdd vérifie core.edit lorsque override est défini (CVE-2026-48947) |
| Super User / RCE | n/a | n/a | Fermé. L'Author reste Author |
J'ai épinglé 6.1.3 après la vague JSST de 2026-07/08. L'API Groups exige déjà core.admin. Le webservice media exige déjà core.edit sur override. Le contrôleur interne, lui, ne le fait toujours pas.
L'Author par défaut possède core.create sur com_media et rien dans core.edit :
{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.delete":{"5":1}}
Le Dispatcher du site laisse passer core.create et charge l'ApiController de l'administrateur. postFiles() ensuite :
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 corrigé (api/.../MediaController.php) :
if ($this->input->json->get('override', false)) {
return $user->authorise('core.edit', 'com_media');
}
Fausses pistes déjà consignées : considérer PUT comme le privilège résiduel (PUT est déjà en 403) ; considérer le chemin webservice comme encore ouvert (CVE-2026-48947 l'a fermé) ; interpréter un 409 sans override comme un échec (c'est le chemin prévu de création-si-absent). Théâtre : un reverse shell. L'oracle est POST override 200 plus PUT 403 plus GET public du PNG témoin.
Autres privilèges résiduels du même tag : API levels, API menu items, articles catid.
python3 joomla-media-override-Abraxas-Labs.py
ou
cd lab
./run.sh
Cible uniquement http://127.0.0.1:19540. Image joomla:6.1.3-php8.3-apache plus 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 même vérification override -> core.edit que MediaController::allowAdd dans ApiController::postFiles() avant createFile(). Conserver PUT sur core.edit.
0f2d4de4729eb951e0878fec98c7990da07915b2)ApiController.php postFilesGNU Affero GPL v3.0. Voir LICENSE. Lab en boucle locale uniquement. Sans garantie.