22 сентября команда WordPress выпустила внеплановый релиз безопасности 7.1.2 и одновременно — исправления для всех поддерживаемых ветвей вплоть до 4.7.37. Причина одна: критическая уязвимость CVE-2026-87902 с оценкой CVSS 9.2. Она позволяет посетителю без какой-либо авторизации заставить WordPress подключить произвольный читаемый PHP-файл за пределами активной темы — а при совпадении нескольких условий превратить это в выполнение произвольного кода на сервере.
Уязвимы все версии с 4.7.0 по 7.1.1 включительно, то есть почти десять лет релизов. Формулировка в официальном анонсе непривычно резкая для проекта, который обычно избегает паники: обновляться рекомендуется немедленно.
Где была ошибка
Проблема жила в wp-includes/template.php, в функции get_page_template() — той самой, которая решает, каким файлом темы отрисовать страницу. WordPress собирает список кандидатов на шаблон, и один из них строится по схеме page-{$pagename}.php, где pagename приходит прямо из запроса.
Ключевой момент: у WordPress есть штатная защита от обхода каталога — функция validate_file(), которая ловит последовательности вроде ../. В той же функции она применялась к другим кандидатам, но конкретно к варианту с pagename её забыли повесить. Классический случай «защита есть, но не на всех дверях».
Дальше срабатывает цепочка, которую подробно описал нашедший дыру исследователь Роберт Рессль (Robert Ressl):
- WordPress принимает переменные запроса
pagenameиpage_idанонимно, в том числе через POST; - двойное кодирование (double encoding — повторное процентное кодирование символов) позволяет последовательностям обхода пройти раннюю санитизацию: на этом этапе они ещё не выглядят как разделители пути;
- валидный
page_idуказывает на реально опубликованную страницу, поэтому запрос доходит до отрисовки, а «лишний»pagenameпросто остаётся в объекте запроса; - уже внутри
get_page_template()к значению применяетсяurldecode()— и закодированные слеши превращаются в настоящие; - путь нормализуется, но загрузчик не проверяет, что итоговый файл остался внутри разрешённых каталогов тем.
Итог: атакующий получает LFI — local file inclusion, подключение локального файла. С ограничением: имя обязано начинаться с page- и заканчиваться на .php.
Как подключение файла становится выполнением кода
Само по себе подключение чужого PHP-файла ещё не даёт атакующему ничего интересного — нужен файл, который при подключении делает что-то полезное для злоумышленника. В продемонстрированной цепочке эту роль играет pearcmd.php из пакетного менеджера PEAR, который до сих пор лежит в образах многих хостингов.
Эксплуатация занимает два запроса. Первым подключается точка входа PEAR, и через её возможность записывать конфигурацию на диск создаётся файл с подконтрольным атакующему PHP-кодом. Вторым запросом этот свежесозданный файл подключается через ту же дыру в WordPress — код выполняется от имени пользователя веб-сервера (в типичной конфигурации www-data).
Два условия, которые решают всё
Тема с каталогом, начинающимся на page-
Поскольку имя кандидата жёстко склеивается с префиксом page-, обход каталога возможен только если в корне активной темы есть папка, чьё имя начинается на page-. Звучит как экзотика, но это не так: page-templates/ — распространённое имя каталога в темах, особенно в тех, что писались во времена классического WordPress. Проверить это — дело одной команды ls в папке темы.
Включённый register_argc_argv
Для перехода от подключения файла к RCE нужна PHP-директива register_argc_argv в положении On. Именно она делает трюк с pearcmd.php рабочим, передавая внутрь «аргументы командной строки» из строки запроса.
И вот здесь новость неприятная: исторически эта директива по умолчанию была включена, и она до сих пор включена в официальных Docker-образах PHP и на многих серверах под cPanel. В PHP 8.5 значение по умолчанию наконец поменяли на Off — документация давно предупреждала, что для не-CLI SAPI директиву стоит отключать по соображениям безопасности. Но парк сайтов на PHP 8.1–8.4 никуда не делся, и на них дефолт прежний.
Полный список предусловий, который приводит исследователь, длиннее: нужна опубликованная публично доступная страница с подходящим page_id, отсутствие назначенного ей кастомного шаблона, доступный для чтения файл-цель, разрешающая политика файловой системы, наличие PEAR с pearcmd.php и записываемый временный каталог.
Почему 9.2 — и почему это не «сгорел весь интернет»
Оценка CVSS измеряет тяжесть последствий для конфигурации, которая условиям удовлетворяет, а не долю сайтов, которые под эти условия попадают. Сам Рессль подчёркивает это прямым текстом: «Влияние на конфигурацию, удовлетворяющую предусловиям, серьёзно, но оценка не является измерением того, как много сайтов WordPress этим предусловиям соответствуют».
Практический вывод из этого ровно один, и он не в сторону расслабления. Условия перечислены публично, PoC опубликован вместе с патчем, а сканировать интернет на предмет тем с каталогом page-templates/ — задача на вечер. По состоянию на день публикации подтверждённых атак в реальном мире не зафиксировано, и записи в каталоге CISA KEV уязвимость не получила, но окно между раскрытием PoC и первыми массовыми сканами обычно измеряется днями, а не неделями.
Что исправили в коде
Патч сделан в два слоя, и это правильный подход:
- Точечная заплатка. К значению
pagenameнаконец применилиvalidate_file()— ту самую проверку, которая уже применялась к соседним кандидатам. - Слой сдерживания. Добавлена новая функция
_wp_is_template_path_allowed(), которая требует, чтобы любой итоговый путь к шаблону лежал внутри разрешённых каталогов тем — независимо от того, откуда этот путь взялся.
Второй пункт важнее первого. Первый закрывает конкретную дыру, второй закрывает целый класс похожих дыр, которые могли бы всплыть в других местах резолвинга шаблонов — включая те, что добавляют плагины и темы.
Что делать владельцу сайта прямо сейчас
- Обновиться. Исправления вышли для всех ветвей: 7.1.2, 7.0.6, 6.9.9, 6.8.10, 6.7.9, 6.6.9 и так далее вплоть до 4.7.37. Если автоматические фоновые обновления включены, скорее всего, сайт уже обновился — но проверить версию в консоли стоит руками.
- Проверить версию, а не факт обновления. На сайтах с отключёнными автообновлениями, с нестандартной сборкой ядра или с «замороженной» версией под чужой плагин обновление не приедет само.
- Посмотреть в тему. Есть ли в корне активной темы каталог, начинающийся на
page-? Если да — вы были в группе повышенного риска, и обновление критично. - Проверить
register_argc_argv. Значение видно вphpinfo()или черезphp -i. Если On и SAPI не CLI — отключите: это разумная гигиена вне зависимости от этой конкретной CVE. - Убрать PEAR, если он не нужен. Наличие
pearcmd.phpв доступных PHP путях — известный «универсальный» шаг эскалации для десятков LFI-уязвимостей, не только этой. - Проверить логи. Искать стоит запросы с подозрительным
pagename, особенно с процентным кодированием внутри значения, и обращения, в которыхpage_idиpagenameприсутствуют одновременно.
Отдельно про бэкпорт до 4.7
Патч уехал в ветки почти десятилетней давности. Это следствие политики WordPress: исправления безопасности бэкпортируются во все ветви, до которых дотягиваются автообновления, даже если официально поддерживается только последняя версия. Для экосистемы, где огромная доля сайтов живёт на давно не обновлявшихся установках, это единственный способ закрыть дыру массово.
Но у этой щедрости есть обратная сторона, которую стоит держать в голове: бэкпорт закрывает конкретную уязвимость и ничего не делает с остальным накопленным техдолгом. Сайт на 4.7 после обновления до 4.7.37 остаётся сайтом на ядре 2016 года со всеми его плагинами и темами — а именно через плагины и приходит основная масса реальных взломов, как это было в истории с массовым взломом через дыру в Breeze.
Главный вывод
CVE-2026-87902 — не уязвимость «в плагине от неизвестного автора», а ошибка в ядре, прожившая с 2016 года в одной из самых горячих функций шаблонизатора. Нашли её не сканером, а вниманием к деталям: кто-то заметил, что в одной функции защита применяется к четырём кандидатам из пяти.
Для владельца сайта практическая часть проста: обновить ядро, выключить register_argc_argv на вебе, вычистить PEAR. Для разработчика вывод чуть шире — новая функция _wp_is_template_path_allowed() означает, что любые кастомные фильтры резолвинга шаблонов в темах и плагинах теперь работают в более жёстких рамках. Если вы подменяли путь к шаблону через template_include или родственные хуки нестандартным способом, стоит прогнать актуальную ветку 7.1 на стейджинге до того, как обновление приедет в продакшен автоматически.
Частые вопросы
Проверить версию руками. WordPress выпустил исправления для всех ветвей вплоть до 4.7.37, и фоновые автообновления обычно доставляют их за часы. Но автообновления часто отключают — плагином, константой WP_AUTO_UPDATE_CORE или политикой хостинга, — и на таких сайтах патч не приедет сам. Нужная версия: 7.1.2 для ветки 7.1, 7.0.6 для 7.0, 6.9.9, 6.8.10, 6.7.9, 6.6.9 для соответствующих веток.
Нужны два условия сразу. Первое: в корне активной темы есть каталог, имя которого начинается на page- (чаще всего это page-templates). Второе: в PHP включена директива register_argc_argv — её видно в phpinfo(). Если хотя бы одного условия нет, продемонстрированная цепочка до выполнения кода не складывается. Это не повод откладывать обновление: само подключение стороннего файла остаётся возможным.
На момент публикации подтверждённых атак в реальном мире не зафиксировано, и в каталог CISA Known Exploited Vulnerabilities уязвимость не попала. При этом рабочий proof-of-concept опубликован исследователем одновременно с патчем, а условия эксплуатации описаны публично, так что появление массовых сканов — вопрос дней.
CVSS оценивает тяжесть последствий для конфигурации, которая условиям удовлетворяет, а не распространённость такой конфигурации. Автор находки прямо подчёркивает это различие: высокий балл говорит о серьёзности компрометации на уязвимом сервере, но ничего не говорит о доле сайтов WordPress, попадающих под все предусловия.
Источники
- 1.WordPress 7.1.2 Release — WordPress Newshttps://wordpress.org/news/2026/09/wordpress-7-1-2-release/
- 2.GHSA-7hp8-65ch-5whp: Unauthenticated path traversal leading to conditional RCE — WordPress Security Advisoryhttps://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
- 3.CVE-2026-87902: Critical WordPress file inclusion and conditional RCE — Robert Resslhttps://ressl.ch/blog/cve-2026-87902-wordpress/
- 4.WordPress 7.1.2 Security Release: Unauthenticated LFI to RCE — Patchstackhttps://patchstack.com/articles/wordpress-7-1-2-security-release-unauthenticated-lfi-to-rce/
- 5.WordPress Issues Patch for Critical Flaw That Can Enable Code Execution on Some Servers — The Hacker Newshttps://thehackernews.com/2026/09/wordpress-issues-patch-for-critical.html
- 6.register_argc_argv INI — PHP.Watchhttps://php.watch/codex/register_argc_argv



