
Joomla 6.1.3 - Высокий - Удалённый - Перезапись - перезапись остатков в медиаменеджере
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · joomla-media-override
Класс: Остаточные привилегии Доступ: Удалённый
Joomla CMS 6.1.3 (0f2d4de) - Open Source Matters
CVE-2026-48947 научил webservice media API требовать core.edit при установленном override. Site JSON API по-прежнему этого не делает. У стандартного Author есть core.create на com_media и нет core.edit. Site dispatcher считает core.create достаточным и загружает администраторский ApiController. Затем postFiles() передаёт override напрямую в createFile().
Стандартный Author может заменить файл, который уже находится на диске. Посетители продолжают обращаться по тому же публичному URL и получают новые байты.
| ID | CVE пока нет |
| Класс | Остаточные привилегии / целостность |
| Доступ | Удалённый (стандартный Author, без API-токена) |
| CWE | CWE-863, CWE-639 |
| CVSS | Средний: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Продукт | Joomla CMS |
| Затронуто | 6.1.3 (0f2d4de4729eb951e0878fec98c7990da07915b2), site com_media JSON API |
| Аутентификация | Стандартный Author (группа 3). Без API-токена. |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Войти как стандартный Author и заменить файл, который уже есть на сайте. Это дефейс / подмена хранимых медиафайлов, а не захват сайта. Denylist расширений не изменён, так что это не загрузка .php.
| Привилегия | Метод / путь | Тип | Доказано в лаборатории или выведено |
|---|---|---|---|
| Перезаписать существующий медиафайл (логотип, изображение статьи) | Site POST /index.php?option=com_media&task=api.files&format=json&path=local-images:/ с JSON override: true | целостность | Доказано в лаборатории. HTTP 200, SHA на диске изменён, публичный GET /images/joomla_black.png отдал новый PNG, содержащий JOOMLA-MEDIA-OVERRIDE-WITNESS |
| Подменить всё, что по-прежнему указывает на этот URL | Тот же публичный путь, без cache-bust | целостность | Доказано в лаборатории, что публичные байты изменились. Выведено для других адаптеров под images/ |
| Создать новый разрешённый файл | Тот же POST без override | предполагаемое поведение | У Author есть core.create на com_media |
| Редактировать через PUT | PUT тот же task, путь включает имя файла | н/д | Доказано в лаборатории, что закрыто. HTTP 403. putFiles() уже требует core.edit |
| Создать, когда имя существует, без override | POST override: false (по умолчанию) | н/д | Доказано в лаборатории, что закрыто. HTTP 409, исходный SHA не изменён до POST с override |
Загрузить .php или другие запрещённые типы | denylist createFile / isMediaFile | н/д | Выведено, что закрыто. Эта лаборатория отправляет только PNG |
| Вызвать webservice media API с override | /api/index.php/v1/media/files | н/д | Выведено, что закрыто. MediaController::allowAdd проверяет core.edit, когда установлен override (CVE-2026-48947) |
| Super User / RCE | н/д | н/д | Закрыто. Author остаётся Author |
Я зафиксировал 6.1.3 после волны JSST 2026-07/08. Groups API уже требует core.admin. Webservice media уже требует core.edit при override. Внутренний контроллер по-прежнему этого не делает.
У стандартного Author есть core.create на com_media и ничего в core.edit:
{"core.admin":{"7":1},"core.manage":{"6":1},"core.create":{"3":1},"core.delete":{"5":1}}
Site Dispatcher пропускает core.create и загружает администраторский ApiController. Затем postFiles():
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 (api/.../MediaController.php):
if ($this->input->json->get('override', false)) {
return $user->authorise('core.edit', 'com_media');
}
Уже зафиксированные неверные пути: считать PUT остаточной уязвимостью (PUT уже возвращает 403); считать путь webservice всё ещё открытым (CVE-2026-48947 его закрыл); воспринимать 409 без override как промах (это и есть предполагаемый путь create-if-absent). Театр: reverse shell. Оракул — это POST override 200 плюс PUT 403 плюс публичный GET witness PNG.
Остатки того же пина: levels API, menu items API, articles catid.
python3 joomla-media-override-Abraxas-Labs.py
или
cd lab
./run.sh
Цель только http://127.0.0.1:19540. Образ joomla:6.1.3-php8.3-apache плюс 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
Та же проверка override -> core.edit, что и в MediaController::allowAdd, внутри ApiController::postFiles() перед createFile(). Оставить PUT на core.edit.
0f2d4de4729eb951e0878fec98c7990da07915b2)ApiController.php postFilesGNU Affero GPL v3.0. См. LICENSE. Только loopback-лаборатория. Без гарантий.