PHP2Shell: две критические уязвимости в ядре PHP, которые не закрывает ни одна CMS
В ядре PHP обнаружены две критические уязвимости, объединённые под названием PHP2Shell. Они работают ниже уровня CMS: обновление WordPress, Drupal или Joomla не помогает. Уязвимыми остаются миллионы серверов, а атаки начались ещё до выхода патча.
PHP2Shell: две дыры в ядре PHP, которые не закрывает ни одна CMS
В конфигурации Apache с mod_php разбор тела HTTP-запроса происходит до того, как управление передаётся файлу сайта. Можно поставить пустой index.php — атака всё равно сработает. Обновление WordPress, Drupal, Joomla или Magento в этой ситуации ничего не решает: ошибка лежит ниже уровня CMS, в самом интерпретаторе. Владелец сайта может делать всё правильно — ставить сложные пароли, настраивать WAF, следить за обновлениями приложения — и оставаться уязвимым.
Две разные поломки в одном ядре
Уязвимости CVE-2026-109618 и CVE-2026-109619, объединённые под названием PHP2Shell, работают по-разному. Официальная оценка PHP — 9,2 балла из 10 по CVSS v4.0. Уязвимый код присутствовал в ядре более 14 лет, механизмы прослеживаются до PHP 5.4 (март 2012 года).
Первая уязвимость — RCE через повреждение памяти при нехватке памяти (OOM). Когда PHP сталкивается с нехваткой памяти, он прерывает обработку запроса через longjmp() — аварийный переход к обработчику ошибки. Проблема в том, что в некоторых местах память уже выделена, но не инициализирована. При завершении запроса интерпретатор обращается к некорректно инициализированным структурам, и повреждение памяти превращается в выполнение произвольного кода.
Довести запрос до OOM можно, не выходя за видимые лимиты. Для этого используется разбор multipart/form-data. Три конкретных приёма: запрос без корректного Content-Length проходит проверку post_max_size; неограниченное количество заголовков у части multipart занимает в памяти больше, чем по сети; пустые поля загрузки файлов не считаются в max_file_uploads, но создают массивы метаданных. Вложенные имена полей дополнительно умножают работу. Разница между размером сетевых данных и объёмом выделяемой памяти означает, что post_max_size не гарантирует защиту.
Вторая уязвимость — use-after-free «протухшего» глобального состояния воркера. Воркер PHP хранит глобальное состояние: ошибки, сессии, объекты, состояние расширений. Иногда эти данные указывают на память конкретного запроса. Если запрос прерван фатальной ошибкой — нехваткой памяти или таймаутом — сброс состояния может не произойти. Память освобождается, указатель остаётся. Следующий запрос в том же воркере обращается к освобождённой памяти.
Атака двухзапросная: первый запрос оставляет «мусор» в глобальном состоянии, второй его использует. Несколько воркеров снижают шанс попасть в тот же процесс, но не устраняют проблему. Подтверждённые векторы — fatal error backtraces (PHP 8.5, mod_php), жизненный цикл сессий (mod_php и PHP-FPM), утечка памяти процесса. Одна из цепочек использует session.upload_progress — механизм, включённый по умолчанию.
Утечка до эмбарго и атаки до патча
Wiz направила отчёт команде PHP 24 августа 2026 года, PHP подтвердила получение 21 сентября. Предварительное уведомление о внеплановом релизе появилось 20 октября, сам релиз назначили на 21 октября 14:00 UTC.
Дальше хронология пошла не по плану. 21 октября в 13:34 UTC информация опубликована в GitHub Security Advisories — за 26 минут до официального срока. Первые упоминания появились на Hacker News (пост пользователя petecooper, впоследствии удалён). Исследователи Calif по ошибке опубликовали видео с работающим PoC-эксплойтом. Chainguard выпустила готовые образы с исправлениями ещё до официальной публикации, что позволяло изучать патч заранее.
Атаки начались до выхода патча. Amazon Threat Intelligence зафиксировала попытку эксплуатации 17 октября на honeypot'е MadPot — за четыре дня до публичного раскрытия. Как злоумышленник узнал об уязвимостях, неизвестно. Cloudflare развернула временные WAF-правила ещё до публичного выпуска исправления. В день выхода патча, 21 октября, были зафиксированы попытки с использованием PoC-эксплойтов — их отмечали Cloudflare, Hadrian, Patchstack, Vega и watchTowr. Подтверждённых успешных взломов на момент публикации источников нет.
Есть соблазн связать утечку деталей до эмбарго с попыткой эксплуатации 17 октября в причинно-следственную цепочку. Оба факта — утечка и ранняя атака — стоят отдельно.
Что на самом деле нашёл ИИ
Первые признаки OOM-уязвимости находили в 2020 и 2024 годах, но тогда не могли доказать возможность RCE. Проблему CVE-2026-109618 нашли в 2024 году во время внешнего аудита — и исправление месяцами буксовало.
В 2026 году две команды независимо довели эту проблему до эксплойта. Calif работала в рамках инициативы OpenAI «Patch the Planet» с моделью GPT-5.6 Sol. Wiz использовала ИИ-агента Wiz Atlas, Gemini 4 Argon и ту же GPT-5.6 Sol. То, что сделали модели, — не «магия», а ускорение анализа давно известной проблемы: они нашли способы управляемо вызывать OOM, обходить post_max_size и анализировать состояние PHP после аварийного завершения. Паттерн обобщили и использовали для поиска аналогичных проблем. Переход от «мы подозреваем, что это эксплуатируемо» к «вот рабочий эксплойт» занял у людей годы, у ИИ — месяцы.
Миллионы серверов и разрыв между масштабом и инструментами
По данным Palo Alto Networks, публично доступных экземпляров, потенциально подверженных уязвимостям, более 22,6 млн. VirusTotal насчитал более 5,1 млн PHP-хостов, из них ~3,8 млн с открытым хотя бы одним портом. PHP работает примерно на 69–70% сайтов. Затронуты все установки непропатченных версий, обрабатывающие веб-запросы: PHP-FPM, Apache с mod_php, php-cgi, FrankenPHP, LiteSpeed LSAPI. Наибольший риск у mod_php: подтверждён эксплойт без аутентификации при лимитах PHP по умолчанию. FrankenPHP по анализу исходников ведёт себя так же. Для PHP-FPM полного эксплойта первой уязвимости не показали, но для CVE-2026-109619 подтверждена RCE через сессии именно для FPM.
Последствия видны по списку отключённых сервисов. Wikipedia и Internet Archive (последний подтвердил отключение, но публично не связывал его с уязвимостями). Нидерландские MijnOverheid, DigiD, сайт нижней палаты парламента и ресурсы национального центра кибербезопасности. Госструктуры Финляндии. Правозащитная организация noyb. HR-платформа BambooHR — с предупреждением клиентов о возможных проблемах с начислением зарплат. Датская платформа школьной коммуникации, сайты местных властей Франции, немецкий ITZBund (без выявленных инцидентов), 4Chan, Something Awful, игра Shadowverse: Worlds Beyond, часть японских e-commerce сайтов. АНБ США рекомендовало отключать сайты, которые невозможно немедленно обновить.
Теперь практическая часть, где масштаб проблемы сталкивается с реальными инструментами её решения.
Вывод php -v в консоли может показывать не ту версию, которая обслуживает сайт. На серверах с ISPmanager, cPanel, CloudLinux или Remi стоит по несколько PHP, и сайт может работать через другую версию php-fpm. Linux-дистрибутивы переносят исправления в старые версии без изменения номера — вывод php -v может показывать «уязвимую» версию при уже залатанном пакете. Проверять нужно через apt changelog или rpm -q --changelog. Заголовок X-Powered-By ненадёжен: его часто скрывают, а версия может не совпадать с реальной. После установки патча обязательно перезапустить все воркеры — php-fpm и Apache, иначе старый код продолжит работать в памяти. В Docker нужно пересобрать образы, а не просто перезапустить контейнеры.
Временные меры и их цена
До установки патча рекомендуют отключить session.upload_progress.enabled = Off. Техническая деталь, о которой легко споткнуться: директива имеет тип PHP_INI_PERDIR и не меняется через ini_set() во время выполнения — только через php.ini, конфигурацию пула FPM или настройки сервера. Второй вариант — блокировка multipart-запросов на уровне Nginx или Apache. Побочный эффект конкретный: блокируются легитимные загрузки файлов. HTTP 403 в ответ на загрузку — ожидаемое поведение временной меры, а не сбой сайта.
Суть в том, что временные меры сужают отдельные векторы, но не закрывают уязвимость. Отключение upload_progress не помогает против CVE-2026-109618 — там достаточно разбора multipart без него. Разработчики PHP прямо говорят: полностью решает проблему только обновление.
Исправленные версии — 8.2.35, 8.3.36, 8.4.27, 8.5.12. Для PHP 7.4, 8.0 и 8.1 подготовлены минимальные бэкпорты исходного кода, но официальных обновлённых бинарных сборок нет. Для PHP 7.3 и старше исправлений не будет вообще.
История PHP2Shell показывает, где находится настоящее узкое место. Патч существует, но добраться до него — задача, требующая координации между хостингом, вендором операционной системы и владельцем сайта. Каждый из них может сделать свою часть работы правильно — и сайт останется уязвимым, потому что кто-то в цепочке не перезапустил воркер или проверил не ту версию. Четырнадцать лет уязвимый код жил в самом массовом языке веб-разработки не потому, что его никто не искал, — его находили и не могли доказать эксплуатируемость. Понадобились ИИ-модели, чтобы превратить подозрение в рабочий эксплойт. Вопрос теперь не в том, сколько серверов уязвимо, а в том, сколько организаций справятся с обновлением до того, как остальные научатся использовать опубликованный PoC.