
Joomla 6.1.3 - Alto - Remoto - Sovrascrittura - sovrascrittura residua del gestore media
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-media-override
Classe: Privilegio residuo Portata: Remota
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
CVE-2026-48947 ha insegnato all'API media del webservice a richiedere core.edit quando override è impostato. L'API JSON del sito ancora no. L'Author predefinito ha core.create su com_media e nessun core.edit. Il dispatcher del sito considera core.create sufficiente e carica l'ApiController dell'amministratore. postFiles() quindi passa override direttamente a createFile().
Un Author standard può sostituire un file già presente su disco. I visitatori continuano a colpire lo stesso URL pubblico e ricevono i nuovi byte.
| ID | nessuna CVE al momento |
| Classe | Privilegio residuo / integrità |
| Portata | Remota (Author predefinito, nessun token 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 |
| Prodotto | Joomla CMS |
| Affetto | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), API JSON com_media del sito |
| Auth | Author predefinito (gruppo 3). Nessun token API. |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Accedere come Author standard e sostituire un file già presente sul sito. Si tratta di defacement / bait-and-switch di media memorizzati, non di takeover del sito. La denylist delle estensioni è invariata, quindi non si tratta di un upload .php.
| Privilegio | Metodo / percorso | Tipo | Provato in lab o dedotto |
|---|---|---|---|
| Sovrascrivere un file media esistente (logo, immagine di un articolo) | POST /index.php?option=com_media&task=api.files&format=json&path=local-images:/ del sito con JSON override: true | integrità | Provato in lab. HTTP 200, SHA su disco modificato, GET pubblico /images/joomla_black.png ha servito il nuovo PNG che contiene JOOMLA-MEDIA-OVERRIDE-WITNESS |
| Deface di qualunque cosa punti ancora a quell'URL | Stesso percorso pubblico, nessun cache-bust | integrità | Provato in lab che i byte pubblici sono cambiati. Dedotto per altri adapter sotto images/ |
| Creare un nuovo file consentito | Stesso POST senza override | previsto | Author ha core.create su com_media |
| Modificare tramite PUT | PUT stessa task, il percorso include il nome del file | n/a | Provato in lab chiuso. HTTP 403. putFiles() richiede già core.edit |
| Creare quando il nome esiste, senza override | POST override: false (predefinito) | n/a | Provato in lab chiuso. HTTP 409, SHA originale invariato fino al POST con override |
Caricare .php o altri tipi negati | denylist di createFile / isMediaFile | n/a | Dedotto chiuso. Questo lab invia solo un PNG |
| Chiamare l'API media del webservice con override | /api/index.php/v1/media/files | n/a | Dedotto chiuso. MediaController::allowAdd verifica core.edit quando override è impostato (CVE-2026-48947) |
| Super User / RCE | n/a | n/a | Chiuso. Author resta Author |
Ho fissato la 6.1.3 dopo l'ondata JSST di 2026-07/08. L'API Groups richiede già core.admin. Il media del webservice richiede già core.edit su override. Il controller interno ancora no.
L'Author predefinito ha core.create su com_media e nulla in core.edit:
{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.delete":{"5":1}}
Il Dispatcher del sito lascia passare core.create e carica l'ApiController dell'amministratore. postFiles() quindi:
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 corretto (api/.../MediaController.php):
if ($this->input->json->get('override', false)) {
return $user->authorise('core.edit', 'com_media');
}
Vicoli ciechi già registrati: considerare PUT come il residuo (PUT è già 403); considerare il percorso webservice ancora aperto (CVE-2026-48947 l'ha chiuso); leggere il 409 senza override come un mancato riscontro (quello è il percorso previsto di create-if-absent). Teatro: una reverse shell. L'oracolo è POST override 200 più PUT 403 più GET pubblico del PNG witness.
Altri residui dello stesso pin: levels API, menu items API, articles catid.
python3 joomla-media-override-Abraxas-Labs.py
oppure
cd lab
./run.sh
Target solo http://127.0.0.1:19540. Immagine joomla:6.1.3-php8.3-apache più 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
Stesso controllo override -> core.edit di MediaController::allowAdd dentro ApiController::postFiles() prima di createFile(). Mantenere PUT su core.edit.
0f2d4de4729eb951e0878fec98c7990da07915b2)ApiController.php postFilesGNU Affero GPL v3.0. Vedi LICENSE. Solo lab su loopback. Nessuna garanzia.