
Joomla 6.1.3 - High - Remote - Overwrite - media manager overwrite leftover
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-media-override
Class: Privilege leftover Reach: Remote
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
CVE-2026-48947 taught the webservice media API to require core.edit when override is set. The site JSON API still does not. Default Author has core.create on com_media and no core.edit. The site dispatcher treats core.create as enough and loads the administrator ApiController. postFiles() then passes override straight into createFile().
A stock Author can replace a file that is already on disk. Visitors keep hitting the same public URL and get the new bytes.
| ID | no CVE yet |
| Class | Privilege leftover / integrity |
| Reach | Remote (default Author, no API token) |
| CWE | CWE-863, CWE-639 |
| CVSS | Medium: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Product | Joomla CMS |
| Affected | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), site com_media JSON API |
| Auth | Default Author (group 3). No API token. |
| License | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 only |
Log in as a stock Author and replace a file that is already on the site. This is defacement / bait-and-switch of stored media, not a site takeover. The extension denylist is unchanged, so this is not a .php upload.
| Privilege | Method / path | Kind | Lab-proven or inferred |
|---|---|---|---|
| Overwrite an existing media file (logo, article image) | Site POST /index.php?option=com_media&task=api.files&format=json&path=local-images:/ with JSON override: true | integrity | Lab-proven. HTTP 200, disk SHA changed, public GET /images/joomla_black.png served the new PNG that carries JOOMLA-MEDIA-OVERRIDE-WITNESS |
| Deface whatever still points at that URL | Same public path, no cache-bust | integrity | Lab-proven the public bytes flipped. Inferred for other adapters under images/ |
| Create a new allowed file | Same POST without override | intended | Author has core.create on com_media |
| Edit via PUT | PUT same task, path includes the filename | n/a | Lab-proven closed. HTTP 403. putFiles() already requires core.edit |
| Create when the name exists, no override | POST override: false (default) | n/a | Lab-proven closed. HTTP 409, original SHA unchanged until the override POST |
Upload .php or other denied types | createFile / isMediaFile denylist | n/a | Inferred closed. This lab only sends a PNG |
| Call the webservice media API with override | /api/index.php/v1/media/files | n/a | Inferred closed. MediaController::allowAdd checks core.edit when override is set (CVE-2026-48947) |
| Super User / RCE | n/a | n/a | Closed. Author stays Author |
I pinned 6.1.3 after the 2026-07/08 JSST wave. Groups API already requires core.admin. Webservice media already requires core.edit on override. The internal controller still does not.
Default Author has core.create on com_media and nothing in core.edit:
{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.delete":{"5":1}}
Site Dispatcher lets core.create through and loads the administrator ApiController. postFiles() then:
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);
Fixed webservice (api/.../MediaController.php):
if ($this->input->json->get('override', false)) {
return $user->authorise('core.edit', 'com_media');
}
Wrong turns already recorded: treating PUT as the leftover (PUT is already 403); treating the webservice path as still open (CVE-2026-48947 closed it); reading 409 without override as a miss (that is the intended create-if-absent path). Theatre: a reverse shell. The oracle is POST override 200 plus PUT 403 plus public GET of the witness PNG.
Same pin leftovers: levels API, menu items API, articles catid.
python3 joomla-media-override-Abraxas-Labs.py
or
cd lab
./run.sh
Target only 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
Same override -> core.edit check as MediaController::allowAdd inside ApiController::postFiles() before createFile(). Keep PUT on core.edit.
0f2d4de4729eb951e0878fec98c7990da07915b2)ApiController.php postFilesGNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.