CVSS 8.8 (HIGH), ноль привилегий, автоматизируемая эксплуатация - по оценке CISA-ADP, Technical Impact: total. CVE-2026-42608 в Grav CMS - path traversal через компонент FormFlash, где единственный POST-параметр
__form-flash-id превращает любую страницу с формой в точку входа. Неаутентифицированный атакующий создаёт произвольные директории и пишет YAML-файлы с контролируемым содержимым прямо в каталоги конфигурации. В русскоязычном сегменте детальный разбор кода этой уязвимости до сих пор не публиковался - закрываю пробел: от уязвимой строки PHP до рабочего HTTP-запроса, с контекстом предыдущих CVE Grav и маппингом на MITRE ATT&CK.FormFlash: уязвимый компонент Grav CMS и поверхность атаки
Grav - flat-file CMS на PHP с 14 500+ звёзд на GitHub и свыше 100 000 загрузок Docker-образа (по данным TantoSec). Вместо базы данных Grav хранит всё в YAML-файлах: конфигурация сайта, учётки пользователей, контент страниц, настройки плагинов - всё лежит на диске. Для понимания импакта path traversal это принципиально: любая запись файла в произвольный каталог - это модификация поведения приложения. Не «потенциальная», а прямая.FormFlash (
Grav\Framework\Form\FormFlash) - внутренний компонент, отвечающий за сохранение данных форм между HTTP-редиректами. Стандартный сценарий: посетитель заполняет контактную форму, шлёт POST - Grav делает redirect (302) на страницу подтверждения, а FormFlash между двумя запросами сбрасывает промежуточные данные во временную директорию на диске. После обработки - удаляет.Критическая деталь: FormFlash принимает идентификатор сессии напрямую из POST-параметра
__form-flash-id. Именно этот параметр определяет путь к директории записи. Компонент доступен на любой странице с Grav Forms: контактная форма, /admin/login, регистрация, любая кастомная форма. Аутентификация не нужна - публичный endpoint.Поверхность атаки определяется тем, что большинство инсталляций Grav с плагином Administrator содержат минимум одну форму. По данным TantoSec, ZoomEye находит порядка 36 000 инстансов по запросу «Grav Admin Login» - каждый из них потенциально уязвим.
Как FormFlash строит путь хранения
Метод__construct() класса FormFlash получает session_id из POST-параметра и использует его при построении пути. Ключевой фрагмент (по данным advisory GHSA-hmcx-ch82-3fv2):
PHP:
$folder = $config['folder']
?? ($this->sessionId
? 'tmp://forms/' . $this->sessionId
: '');
$this->folder = $folder && $locator->isStream($folder)
? $locator->findResource($folder, true, true)
: $folder;
$this->sessionId - значение __form-flash-id из POST-запроса. Оно конкатенируется напрямую со строкой 'tmp://forms/' без какой-либо проверки на управляющие символы пути. Дальше findResource() разрешает stream-URI в реальный путь файловой системы. Если sessionId содержит последовательности ../, разрешённый путь выходит за границы tmp/forms/ и попадает в произвольные каталоги webroot.Причина - отсутствие вызова
basename() или regex-валидации перед конкатенацией. Разработчики обращались с session_id как с доверенным значением: «ну строка из POST, она же не в SQL попадает - какой вред?». В flat-file архитектуре ответ другой.Path traversal без аутентификации: анатомия CVE-2026-42608
Уязвимость классифицирована как CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). Разберу CVSS-вектор по компонентам - здесь CVSS 4.0:- AV:N - атака через сеть, стандартный HTTP POST
- AC:L - сложность низкая, специальных условий нет
- AT:N - не требует предварительных действий
- PR:N - привилегии не нужны (unauthenticated)
- UI:N - взаимодействие пользователя не требуется
- VC:H / VI:H - высокое влияние на конфиденциальность и целостность
- VA:N - прямого влияния на доступность нет (хотя через inode exhaustion возможен DoS)
E:P- есть proof-of-concept
poc, Automatable: yes, Technical Impact: total. Решение - Track* (следить, готовить патч).Что записывается на диск
При успешной эксплуатации FormFlash создаёт:- Произвольную директорию по пути, заданному через traversal-последовательность
- Файл
index.yamlвнутри этой директории с данными, частично контролируемыми атакующим
index.yaml формируется из данных формы: поля form, id, unique_id и пользовательских данных из POST-параметров form-data[*]. Атакующий контролирует и путь записи, и часть содержимого файла - в рамках YAML-формата. Это уже Configuration Injection.Каталоги Grav CMS в зоне доступа
Поскольку Grav хранит всё в файловой системе, traversal открывает доступ к критичным каталогам:| Каталог | Содержимое | Импакт при записи |
|---|---|---|
user/config/ | Конфигурация сайта, плагинов, тем | Configuration Injection - изменение поведения приложения |
user/accounts/ | YAML с хешами паролей, 2FA-секретами, токенами сброса | Подмена/порча учётных данных |
user/pages/ | Контент страниц в Markdown/Twig | Инъекция контента, потенциальный SSTI |
cache/ | Кеш шаблонов и маршрутов | Инвалидация кеша, инъекция в скомпилированные шаблоны |
tmp/ | Временные файлы | Межсессионная коррупция данных |
Запись в
user/config/ - самый жирный вектор: Grav загружает конфигурацию из YAML-файлов при каждом запросе. Инъекция настроек в подкаталог плагина может изменить его поведение - от включения отладочного режима до модификации маршрутов. По классификации OWASP Top 10 это одновременно A01:2021 (Broken Access Control - нет проверки аутентификации для критичной операции записи) и A03:2021 (Injection - traversal как инъекция в путь файловой системы).Grav CMS эксплойт: воспроизведение уязвимости пошагово
Требования к окружению
- ОС: любая с поддержкой Docker (Linux, macOS, Windows с WSL2)
- RAM: минимум 512 МБ для контейнера Grav
- Grav CMS: v1.7.49.5 - последний стабильный релиз на момент обнаружения уязвимости (уязвим). Рекомендуется официальный Docker-образ
- Инструменты: Burp Suite,
curlили любой HTTP-клиент с возможностью ручного формирования POST-тела - Обязательное условие: наличие хотя бы одной страницы с Grav Form (стандартная установка с плагином Administrator содержит
/admin/login) - Сеть: HTTP-доступ к целевому инстансу
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Эксплуатация воспроизводима на 100% - детерминистически, без race condition и без временных окон. По данным advisory GHSA-hmcx-ch82-3fv2, надёжность подтверждена на v1.7.49.5 (latest stable) и на текущей ветке разработки (март 2026).
Цепочка атаки: от path traversal до unauthenticated RCE в Grav
CVE-2026-42608 сам по себе даёт создание директорий и запись YAML-файлов. Прямого выполнения кода нет. Но в контексте полной цепочки атаки на Grav CMS эта уязвимость становится звеном, которое серьёзно снижает порог входа - Exploit Public-Facing Application (T1190, Initial Access по MITRE ATT&CK).Три вектора развития атаки
Configuration Injection. Grav динамически загружает конфигурацию изuser/config/. Запись index.yaml в подкаталог плагина или темы может изменить настройки маршрутизации, обработки шаблонов, авторизации. В flat-file архитектуре это эквивалент SQL-инъекции в таблицу конфигурации - только через файловую систему. Звучит экзотично, но работает так же надёжно.Cross-User Session Corruption. Контролируя
session_id, атакующий перезаписывает временные данные форм других пользователей - ломает изоляцию сессий. Если администратор заполняет форму в панели управления, атакующий может подменить его промежуточные данные.Denial of Service через inode exhaustion. Каждый POST-запрос создаёт новую директорию. На файловых системах с лимитом inodes (ext4 с дефолтными настройками) массовая отправка запросов исчерпает inodes - сервер перестанет создавать файлы. Для shared-хостингов это особенно больно.
Историческая цепочка CVE в Grav CMS
У Grav CMS задокументированная история path traversal и связанных уязвимостей. В совокупности они формируют цепочку от unauthenticated access до полного RCE (по данным исследования TantoSec):CVE-2024-27921 (CVSS 8.8 HIGH, CWE-22) - path traversal при загрузке файлов в версиях до 1.7.45. Аутентифицированный пользователь с низкими привилегиями может заменять или создавать файлы с расширениями
.json, .zip, .css, .gif. EPSS: 0.0879 - входит в Top 10% по вероятности эксплуатации (92-й перцентиль), что намекает на реальное использование. CISA-ADP: Automatable: no, Technical Impact: total.CVE-2024-34082 (CVSS 8.5 HIGH, CWE-269 - Improper Privilege Management) - чтение произвольных серверных файлов через Twig-синтаксис в версиях до 1.7.46. Пользователь с правами редактирования страниц читает
user/accounts/*.yaml, где лежат хеши паролей, секреты 2FA и токены сброса. CISA-ADP: Technical Impact: total.Цепочка эксплуатации, описанная TantoSec: Password Reset Poisoning (через Host header на
/admin/forgot) → захват учётной записи администратора → CVE-2024-34082 (чтение учётных данных через SSTI) → CVE-2024-27921 (загрузка файлов через path traversal) → злоупотребление Scheduler для выполнения кода на сервере.CVE-2026-42608 добавляет новый вектор в начало этой цепочки: он работает полностью без аутентификации и не требует SMTP (в отличие от Password Reset Poisoning). Там, где почтовый сервер не настроен, именно CVE-2026-42608 даёт альтернативную точку входа для модификации файловой системы.
Маппинг на MITRE ATT&CK
| Этап | Техника | ID | Контекст в Grav |
|---|---|---|---|
| Initial Access | Exploit Public-Facing Application | T1190 | CVE-2026-42608: FormFlash path traversal |
| Discovery | File and Directory Discovery | T1083 | Перечисление структуры через traversal |
| Collection | Data from Local System | T1005 | Чтение YAML-конфигов и учёток (CVE-2024-34082) |
| Persistence | Web Shell | T1505.003 | Запись файлов в webroot через цепочку уязвимостей |
| Privilege Escalation | Valid Accounts | T1078 | Захват учётных данных из YAML-файлов |
| Execution | Unix Shell | T1059.004 | RCE через Scheduler при admin-доступе |
Цепочка проходит от Initial Access (T1190) через Discovery и Collection до Persistence и Execution - шесть тактик ATT&CK одним набором уязвимостей в одной CMS.
Ограничения path traversal в Grav CMS и возможности детекта
Когда эксплуатация CVE-2026-42608 не работает
Ограничение прав web-сервера. Если PHP/Apache/Nginx запущен с минимальными привилегиями, аuser/config/ и user/accounts/ принадлежат root с permissions 0750 - запись не пройдёт. На практике большинство установок через Docker-образ используют www-data как владельца всей webroot, и никаких ограничений нет. Hardened-инсталляции на bare metal с разделением привилегий - исключение, не правило.Отсутствие форм. Grav как статический генератор без плагина Admin и без пользовательских форм не предоставляет поверхности атаки. Конфигурация нетипичная - по данным TantoSec, большинство production-инсталляций используют Administrator plugin.
Reverse proxy с URL-нормализацией. Nginx с
merge_slashes on или Cloudflare с WAF может нормализовать или отклонить запросы с ../ в теле. Но параметр __form-flash-id передаётся в POST body (не в URL), и далеко не все WAF инспектируют POST-параметры с одинаковой глубиной. Тут как повезёт.Подходы к детекту
На уровне WAF. Мониторинг POST-параметров на последовательности../ и их кодированные варианты (..%2f, %2e%2e/) в параметре __form-flash-id. ModSecurity CRS правило 930100 покрывает базовый случай, но для Grav эффективнее таргетированное правило на конкретный параметр - меньше false positive от легитимных форм.На уровне файловой системы. Настроить auditd или inotify на мониторинг создания файлов в
user/config/, user/accounts/, user/pages/. Появление index.yaml в неожиданных подкаталогах - прямой IoC. В нормальной работе Grav формы пишут только в tmp/forms/<session_uuid>/.На уровне логов. POST-запросы с
__form-flash-id, содержащим символы /, \ или .. - аномалия. В штатной работе этот параметр содержит UUID сессии (строго алфавитно-цифровую строку). Простой grep по access-логам с регулярным выражением на traversal-паттерны в POST-данных выявляет попытки эксплуатации.Патч и Grav CMS безопасность: как закрыли CVE-2026-42608
Исправление применено в ветке Grav 2.0 - коммитd904efc33, включённый в релиз 2.0.0-beta.2 (по данным advisory GHSA-hmcx-ch82-3fv2, дата фикса - 2026-04-24).Суть патча:
FormFlash::[B]construct() теперь валидирует session_id, unique_id и id через строгий allowlist - регулярное выражение [A-Za-z0-9,_-]{1,64}. Значения, не прошедшие валидацию, сбрасываются в пустую строку. При пустом идентификаторе методы save(), delete() и getTmpDir() становятся no-op: POST-запрос с [/B]form-flash-id=../../user/config/proof_dir просто ничего не создаёт на диске. Один regex - и уязвимости нет.Патч сопровождается 32 unit-тестами в файле
FormFlashSecurityTest.php, покрывающими оригинальный PoC и его вариации (двойное кодирование, альтернативные разделители, edge cases длины). Тут разработчики молодцы - закрыли не только дырку, но и все очевидные обходы.Защитный чеклист
- Обновить Grav до 2.0.0-beta.2 или более поздней версии. Для ветки 1.7.x патч не портирован - если обновление невозможно, применить workaround через WAF
- Права файловой системы: выставить
user/config/иuser/accounts/в chmod 0750 с владельцем, отличным от процесса web-сервера (где архитектура позволяет) - WAF-правило: блокировать POST-запросы с
../,..%2f,%2e%2e/в параметре__form-flash-id - Мониторинг: auditd/inotify на создание файлов в
user/config/вне стандартной административной активности - Минимизация поверхности: если формы на публичных страницах не используются - отключить плагин Form или ограничить его аутентифицированными пользователями через конфигурацию маршрутов
basename() или regex решает проблему), а устойчивый паттерн, который я вижу при аудите PHP-кода flat-file CMS. Разработчики систематически обращаются с идентификаторами сессий как с безопасными значениями: «строка из cookie/POST, она не попадает в SQL - какой от неё вред?». В flat-file архитектуре ответ другой: любой пользовательский ввод, участвующий в конструкции пути файловой системы - прямой вектор для path traversal. Grav не уникален: тот же паттерн встречается в других CMS с файловым хранилищем, просто у Grav уязвимый код оказался на unauthenticated endpoint с формами на каждом инстансе.EPSS для CVE-2026-42608 пока составляет 0.0012 (30-й перцентиль) - формально низкая вероятность массовой эксплуатации. Но CISA-ADP маркирует уязвимость как Automatable:
yes - а значит, массовое сканирование через Shodan/ZoomEye по сигнатуре «Grav Admin Login» с автоматической отправкой PoC-запроса к каждому из 36 000 инстансов это вопрос не «если», а «когда». Пока EPSS не догнал реальность, окно для патча ещё открыто. Если интересно посмотреть как подобный path traversal ломается на живом стенде - лаба по web-эксплуатации на HackerLab.pro даёт именно этот формат.