
Независимый анализ первопричины и proof-of-concept для неаутентифицированной SQL-инъекции с последующим выполнением произвольного кода (RCE) в WordPress (CVE-2026-63030 + CVE-2026-60137), с Docker-лабораторией и подробной документацией цепочки эксплуатации.
Ядро WordPress раскрыло wp2shell 2026-07-17 как критическую неаутентифицированную RCE, затрагивающую стандартные установки (WordPress 6.9.0-6.9.4, 7.0.0-7.0.1, без каких-либо плагинов). Первоначальный исследователь (Searchlight Cyber) не раскрыл технические детали на момент публикации. Этот репозиторий документирует независимый анализ первопричины, полученный исключительно путём сравнения исходного кода ядра WordPress между уязвимой (6.9.4) и исправленной (6.9.5) версиями, а также практическую проверку в локальной лаборатории.
Авторство: воспроизведение здесь повторяет форму запроса из sergiointel/wp2shell-poc, подтверждена побайтово и сверена с реальным диффом патча.
root_cause_analysis.md — полный разбор: две связанные уязвимости, точный
уязвимый код, дифф патча, цепочка эксплуатации и практическая проверка
«до/после» на реальном экземпляре WP 6.9.4.poc_upstream.py — неизменённая копия оригинального PoC от sergiointel (только SQLi).poc_upstream_rce.py — PoC sergiointel, обновлённый примерно через 22 часа после
публикации: добавлены неаутентифицированное повышение привилегий и RCE без
взлома каких-либо учётных данных. См. раздел 7 в root_cause_analysis.md.poc_extract.py / poc_extract2.py — настроенный вариант (большее значение SLEEP(),
более высокий порог тайминга, более длинный потолок поиска символов), необходимый для получения
надёжного сигнала в лаборатории с Docker/прокси, где значение SLEEP(0.15) по умолчанию
из исходного скрипта терялось из-за сетевых задержек.docker-compose.yml — поднимает ту же лабораторию WordPress 6.9.4 + MariaDB,
использовавшуюся для проверки.payload.json, response.json, response_patched.json — вручную созданный
запрос, повторяющий структуру PoC, и необработанные ответы сервера
до и после применения файлов патча 6.9.5.Второй, независимо написанный PoC (github.com/Icex0/wp2shell-poc) был полностью изучен
и независимо описывает тот же механизм первопричины, подтверждая
приведённый ниже анализ. С помощью его контентной (логической, а не основанной на таймингах)
слепой SQL-инъекции хэш учётных данных администратора этой лаборатории был восстановлен
со 100% точностью, и с использованием этих известных учётных данных была продемонстрирована
вживую полная цепочка SQLi-to-RCE: от SQL-инъекции к извлечению учётных данных,
затем к авторизованной загрузке плагина с веб-шеллом и к выполнению кода от имени www-data.
См. раздел 6 в root_cause_analysis.md.
Путь со взломом учётных данных реален, но он не единственный. PoC от sergiointel
впоследствии был обновлён для достижения неаутентифицированной RCE вообще без
взлома учётных данных: с помощью UNION SELECT создаются поддельные строки базы данных,
которым собственный код WordPress (Customizer/nav-menu/oEmbed-cache) доверяет настолько,
что позволяет неаутентифицированному запросу на создание пользователя пройти успешно.
См. раздел 7 в root_cause_analysis.md. Одной лишь неаутентифицированной SQL-инъекции
достаточно для полной RCE на стандартной установке.
CVE-2026-63030 (путаница пакетных маршрутов REST, CWE-436):
WP_REST_Server::serve_batch_request_v1() добавляет элемент в массив $matches[]
простым push, но пропускает добавление, когда путь подзапроса не удаётся разобрать
(например, намеренно повреждённая запись "http://:"). Этот единственный пропуск
рассинхронизирует $matches[] и $requests[] на один индекс для каждой
последующей записи. Во время диспетчеризации $matches[$i] больше не соответствует
$requests[$i], поэтому код выполняет данные запроса в обработчике другого маршрута,
а не того, относительно которого запрос фактически проверялся.
CVE-2026-60137 (SQL-инъекция): WP_Query::get_posts() выполнял
санитизацию absint() для author__not_in только внутри ветки is_array(). Скалярное
строковое значение полностью пропускало санитизацию и напрямую конкатенировалось
в ... post_author NOT IN ($value).
Связка: ошибка путаницы маршрутов позволяет атакующему провести запрос, объявленный
для /wp/v2/categories (который не распознаёт author_exclude и поэтому
никогда не санитизируется), фактически через контроллер posts (который
читает author_exclude и передаёт его в WP_Query). Аутентификация не
требуется.
По поводу утверждения об RCE: вживую подтверждено, что неаутентифицированная слепая
SQL-инъекция работает и может извлекать произвольные данные БД, включая wp_users.user_pass.
Один проход извлечения на основе таймингов в виртуализированной лаборатории содержал заметные
ошибки на уровне битов (в тестах здесь точность ~94% на символ); контентный
(логический) оракул был лишён этого недостатка и восстановил то же поле
со 100% точностью. RCE была подтверждена двумя различными путями: взлом восстановленного
хэша пароля (зависит от стойкости пароля) и неаутентифицированная цепочка повышения привилегий
с использованием поддельных строк БД, созданных через UNION, которым доверяет
собственный код WordPress (Customizer/nav-menu/oEmbed-cache)
(безусловно, без компрометации учётных данных). См. раздел 7 в
root_cause_analysis.md.
docker compose up -d
# wait for WordPress install wizard to be reachable on :8890, then complete setup
HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 \
python3 poc_extract2.py http://localhost:8890 "SELECT DATABASE()"
Полное техническое описание см. в root_cause_analysis.md.
WordPress опубликовал официальные исправления: 6.9.5 и 7.0.2 (7.1 Beta 2 для бета-ветки), выпущенные 2026-07-17. Немедленно обновитесь, если вы используете затронутую версию. Этот репозиторий публикуется в защитных и образовательных целях после того, как патч уже стал общедоступным. Запускайте любой код из этого репозитория только на системах, которыми вы владеете или на тестирование которых вам явно разрешено.