
Плагин Gravity Forms для WordPress (протестирован до версии 2.9.28) уязвим к неаутентифицированному отражённому межсайтовому скриптингу (XSS) через параметр `form_ids` в AJAX-действии `gform_get_config`.
form_ids в gform_get_config| Поле | Значение |
|---|---|
| Затронутое ПО | Gravity Forms (плагин WordPress) |
| Вендор | Rocketgenius, Inc. |
| Тип уязвимости | CWE-79: Некорректная нейтрализация входных данных при генерации веб-страницы (отражённый межсайтовый скриптинг) |
| Цепочка CWE | CWE-20 → CWE-116 → CWE-838 → CWE-79 (см. анализ CWE ниже) |
| Затронутые версии | Подтверждено для 2.9.28 (последняя версия на момент обнаружения); более ранние версии, вероятно, также затронуты |
| Исправленная версия | 2.9.30.1 (горячий фикс) |
| Оценка CVSS 3.1 | 6.1 (Средняя) |
| Вектор CVSS 3.1 | AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N |
| Требуется аутентификация | Нет (неаутентифицированная) |
| Взаимодействие с пользователем | Требуется (жертва должна посетить страницу, контролируемую атакующим, или перейти по специально созданной ссылке) |
| Обнаружил | Anthony Cihan — Obviam |
| Дата обнаружения | 2026-03-04 |
| Дата раскрытия | 2026-03-18 |
| ID CVE | CVE-2026-4406 |
Плагин Gravity Forms для WordPress (протестирован до версии 2.9.28) уязвим к неаутентифицированному отражённому межсайтовому скриптингу (XSS) через параметр form_ids в AJAX-действии gform_get_config. Уязвимость существует из-за того, что предоставленные пользователем значения form_ids отражаются дословно в HTTP-ответе сервера без какой-либо санитизации, кодирования или экранирования вывода. Ответ передаётся с заголовком Content-Type: text/html; charset=UTF-8, из-за чего браузер разбирает и отображает отражённое содержимое как HTML, включая любые внедрённые элементы скриптов.
Неаутентифицированный атакующий может использовать эту уязвимость для выполнения произвольного JavaScript в контексте происхождения целевого сайта WordPress. Поскольку действие gform_get_config требует действительный config_nonce, а этот nonce публично встроен в HTML-исходный код каждой страницы, загружающей форму Gravity Forms, атакующий может без труда получить действительный nonce, запросив любую публичную страницу целевого сайта перед созданием запроса эксплойта.
Успешная эксплуатация позволяет атакующему похищать сессионные cookie, выполнять действия от имени аутентифицированных пользователей (включая администраторов WordPress), перенаправлять пользователей на вредоносные сайты, изменять содержимое страниц или устанавливать постоянный доступ через создание административной учётной записи.
POST /wp-admin/admin-ajax.php
### Уязвимое действие```
gform_get_config
Параметр args POST-запроса принимает JSON-объект, содержащий массив form_ids. Значения в этом массиве используются в качестве ключей объекта в структуре JSON-ответа без санитизации:```json
{"form_ids":["ATTACKER_CONTROLLED_VALUE"]}
### Response Behavior
Сервер обрабатывает значения `form_ids` и отражает их как ключи JSON внутри тела ответа. Ответ оборачивается в маркеры HTML-комментариев и отдаётся как `text/html`:```
Content-Type: text/html; charset=UTF-8
<!-- gf:json_start -->{"success":true,"data":{"common":{"form":{"pagination":{"ATTACKER_CONTROLLED_VALUE":null}}}}}<!-- gf:json_end -->
form_ids не проверяются как целые числа, не очищаются и не фильтруются. Сервер принимает произвольное строковое содержимое, включая HTML-теги и обработчики событий.form_ids отражаются в теле ответа без кодирования HTML-сущностей. Такие символы, как <, >, " и ', проходят без изменений.Content-Type: text/html; charset=UTF-8, что указывает браузеру анализировать тело ответа как HTML. Любые HTML-теги внутри отраженного значения создаются и отображаются HTML-парсером браузера.config_nonce, требуемый действием gform_get_config, встраивается в объект конфигурации JavaScript (gform_theme_config) на каждой странице, загружающей форму Gravity Forms. Этот nonce одинаков на всех страницах и не привязан к конкретной пользовательской сессии, что делает его тривиально доступным для неаутентифицированных пользователей.Эта уязвимость является результатом нескольких взаимосвязанных слабостей, которые объединяются в цепочку и создают эксплуатируемое условие. Хотя CWE-79 является основной классификацией для целей отчетности CVE, полная цепочка показывает, как каждый сбой усугубляет ситуацию и способствует эксплуатации.
CWE-20 CWE-116 CWE-838 CWE-79 Improper Input → Improper Encoding → Inappropriate Encoding → Cross-Site Validation or Escaping of Output for Output Context Scripting (XSS) [EXPLOITABLE] form_ids accepts Reflected values are JSON data served as Browser parses arbitrary strings not HTML-entity text/html instead of injected HTML tags instead of integers encoded in response application/json and executes JS
### CWE-20: Некорректная проверка входных данных (Способствующая)
**Роль:** Корневой фактор, позволяющий вредоносным данным попадать в конвейер обработки.
Параметр `form_ids` в JSON-объекте `args` должен содержать числовые идентификаторы форм, но принимает произвольную строку без какой-либо проверки. Не применяются ни проверка типа (`intval()`), ни фильтрация по регулярному выражению (`^[0-9]+$`), ни сравнение с белым списком известных идентификаторов форм, ни ограничения по длине.
**Доказательство:** Сервер принимает и обрабатывает значения `form_ids`, содержащие HTML-теги, обработчики событий JavaScript и произвольные символы Unicode, без отклонения.```json
{"form_ids":["<svg onload=alert(1)>"]} ← Accepted
{"form_ids":["3"]} ← Expected
Роль: Основная уязвимость — непосредственная причина возникновения условия XSS.
Когда сервер формирует JSON-ответ, содержащий значения form_ids, он не применяет к выводимым данным HTML-кодирование сущностей. Символы, имеющие специальное значение в HTML (<, >, ", ', &), передаются в тело ответа без изменений. PHP-функции htmlspecialchars(), esc_html(), wp_json_encode() с параметром JSON_HEX_TAG или эквивалентные функции кодирования вывода не применяются к значениям form_ids перед их записью в ответ.
Доказательство: Литеральная строка <svg onload=alert(document.domain)> появляется в теле ответа байт-в-байт идентичной входным данным, а не в виде <svg onload=alert(document.domain)>.
Роль: Эскалация контекста — превращает проблему отражения данных в инъекцию исполняемого кода.
Тело ответа содержит данные в формате JSON, но обслуживается с заголовком Content-Type: text/html; charset=UTF-8. Это объявление Content-Type указывает HTML-парсеру браузера обрабатывать всё тело ответа как HTML-документ. Если бы ответ обслуживался как application/json, браузер отобразил бы ответ как обычный текст, и никакого HTML-парсинга не произошло бы — внедрённые теги отображались бы как литеральный текст, а не создавались как DOM-элементы.