PHP2Shell (CVE-2026-109618/109619): как мы закрыли уязвимость виртуальным патчем на границе сети
Два критических бюллетеня PHP позволяют выполнить код удалённо без аутентификации через аварийный выход при нехватке памяти и устаревшее глобальное состояние. Рассказываем, как работают уязвимости, что Yeap WAF блокирует на границе с первого дня и что вам всё ещё нужно исправить на своём источнике.

21 октября 2026 года проект PHP опубликовал сразу два бюллетеня безопасности уровня Critical: GHSA-w8hw-w9fx-j79w (CVE-2026-109618) и GHSA-6wmf-4rv5-p3rp (CVE-2026-109619), обе с оценкой 9.2 по CVSS v4. Каждая из них позволяет удалённо выполнить код без аутентификации. Эту пару уязвимостей назвали PHP2Shell.
Затронуты все четыре поддерживаемые ветки (PHP 8.2, 8.3, 8.4 и 8.5), а команда PHP подтвердила, что mod_php эксплуатируем при настройках по умолчанию. Для любого сайта на WordPress, Laravel или другом PHP-приложении одного анонимного POST-запроса может оказаться достаточно, чтобы захватить сервер.
В тот же день, когда вышли бюллетени, мы развернули виртуальный патч против публично известного вектора атаки на всех граничных узлах Yeap CDN. В этой статье мы расскажем, как устроены уязвимости, что блокируют наши правила, чего они заблокировать не могут и что вам всё ещё нужно сделать на своём источнике.

Что это за уязвимости
CVE-2026-109618: повреждение памяти при аварийном выходе по OOM
Когда PHP исчерпывает память, он вызывает longjmp(), чтобы прервать текущий запрос. В некоторых местах память запроса становится доступной ещё до полной инициализации. Если выделение памяти завершится неудачей именно в этот момент, досрочный выход оставит после себя некорректную память, а завершение запроса позже прочитает её и выполнит ветвление по её содержимому.
Злоумышленник может надёжно вызвать OOM в рамках обычных лимитов PHP с помощью некорректного запроса multipart/form-data: без пригодной длины содержимого, с частью, содержащей огромное число заголовков, с пустыми полями файлов, которые обходят max_file_uploads, или с глубоко вложенными именами полей. Официальное исправление полностью убирает longjmp() при OOM. Теперь PHP корректно завершает работу в следующей безопасной точке и немедленно вызывает _exit(), если потребление памяти превышает более высокий порог.
CVE-2026-109619: устаревшее глобальное состояние между запросами
Глобальное состояние, принадлежащее запросу (состояние ошибок, сессий, объектов и расширений), может сохраняться в рабочем процессе после того, как память этого запроса уже освобождена. Некоторый код читает это состояние до того, как следующий запрос будет корректно инициализирован, и в результате переходит по указателю в освобождённую память: это use-after-free.
- Производители (запросы, оставляющие после себя устаревшее состояние): фатальные ошибки, превышение времени выполнения, неудачный
require, нехватка памяти, а такжеfatal_error_backtracesв PHP 8.5. - Потребители (запросы, которые его читают): диагностика INI, применяемая до запуска запроса, и обработка
session.upload_progress, которая включена по умолчанию.
Когда производитель и потребитель попадают на один и тот же рабочий процесс, злоумышленник может сформировать кучу так, чтобы превратить use-after-free в выполнение кода. Команда PHP подтвердила работоспособность пути через upload progress как для mod_php, так и для PHP-FPM.
Затронутые версии
| Ветка | Уязвимы | Исправлено в |
|---|---|---|
| PHP 8.5 | < 8.5.12 | 8.5.12 |
| PHP 8.4 | < 8.4.27 | 8.4.27 |
| PHP 8.3 | < 8.3.36 | 8.3.36 |
| PHP 8.2 | < 8.2.35 | 8.2.35 |
Эксплуатируемость mod_php подтверждена. По результатам анализа исходного кода, FPM, FrankenPHP и LSAPI также уязвимы. Версии с истёкшим сроком поддержки не получат официального патча, поэтому защита перед ними становится ещё важнее.
Что может заблокировать WAF
Публично известный эксплойт по сути представляет собой один неаутентифицированный multipart POST-запрос. Он содержит поле PHP_SESSION_UPLOAD_PROGRESS и загрузку файла, чтобы попасть в обработчик upload progress, а также большое количество полей формы, которые распыляют кучу и занимают освобождённую память. Такая форма запроса видна на уровне HTTP, поэтому его можно остановить ещё до того, как он дойдёт до PHP.
Мы добавили в движок OWASP CRS в Yeap WAF набор самостоятельных правил виртуального патча. Они распознают сочетание триггера upload progress и распыления кучи, на котором держится эксплойт, и возвращают 403 ещё до того, как PHP разберёт запрос.
- Они блокируют самостоятельно. Правила не зависят от оценки аномалий, поэтому уровень паранойи сайта и порог оценки их не ослабляют.
- Они смотрят на поведение, а не на фиксированную сигнатуру. Переименование полей или изменение формы нагрузки не помогает их обойти.
- Они не мешают настоящим загрузкам. Легитимные формы загрузки, в том числе с индикатором прогресса, продолжают работать.
- Они работают на границе сети. На источнике ничего не нужно устанавливать и не нужно менять ни одной строки конфигурации.

От бюллетеня до защиты на каждом узле

Перед выпуском мы проверили правила на вариантах атакующего трафика и на нескольких видах легитимных загрузок: все варианты атаки были заблокированы, а все легитимные загрузки прошли. Затем правила были скомпилированы в наш пакет CRS в виде плагина и включены на всех граничных узлах в 23:34 (UTC+8) 21 октября. Ранним утром 22 октября мы обновили весь пакет до OWASP CRS v4.30.0, и виртуальный патч вошёл в его состав.
Такая скорость обеспечивается конвейером обновлений Yeap WAF. Новый пакет компилируется и проверяется в фоновом режиме, затем попадает на канареечные узлы, где мы следим за состоянием загрузки и долей блокировок. Повсеместно развёртывается только исправный пакет. Граничные узлы заменяют правила атомарно: старые правила продолжают работать, пока новые не будут готовы, поэтому разрывов в защите не бывает, а откатиться к последней рабочей версии можно одним щелчком.
WAF — это не патч: что вам всё ещё нужно сделать
Скажем прямо: WAF может остановить только ту часть атаки, которая передаётся в теле HTTP-запроса. У CVE-2026-109619 есть также производители и потребители, которые вообще не связаны с multipart, например fatal_error_backtraces в PHP 8.5 или предупреждения INI при запуске, а освобождение и использование памяти могут быть разнесены по двум запросам, каждый из которых выглядит совершенно обычно. Это можно исправить только внутри движка PHP. Кроме того, варианты, основанные на времени, например медленные POST-запросы, контентный WAF увидеть не может.
Рассматривайте правила на границе как первую линию обороны, которая даёт вам время, и как можно скорее выполните следующие шаги:
- Обновите PHP до 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 или новее. Согласно бюллетеням, это единственное полное исправление.
- Ужесточите php.ini до обновления, затем перезапустите все рабочие процессы PHP:
; Отключите, если вы не используете индикаторы прогресса загрузки ; (бюллетень рекомендует держать параметр выключенным и после установки патча) session.upload_progress.enabled = 0 ; PHP 8.5: устраняет подтверждённый источник RCE fatal_error_backtraces = 0 ; Консервативные ограничения на тело запроса и входные данные post_max_size = 8M max_input_vars = 1000 max_input_nesting_level = 16 - Для PHP-FPM рассмотрите
pm.max_requests = 1. Каждый рабочий процесс перезапускается после одного запроса, что разрывает повторное использование состояния между запросами. Это снижает производительность, поэтому используйте эту меру как краткосрочную экстренную. - Отклоняйте multipart там, где загрузки не нужны. В консоли Yeap CDN перейдите в раздел Межсетевой экран → Пользовательские правила и создайте правило блокировки из условий «метод — POST», «заголовок запроса Content-Type содержит multipart/form-data» и ваших путей, разрешив только те конечные точки, которые действительно принимают загрузки.
- Ограничьте частоту POST-запросов, чтобы замедлить сканирование и повторные зондирования.
Чтобы проверить своё окружение:
php -v
php -i | grep -E "session.upload_progress.enabled|fatal_error_backtraces"
CLI и веб-сервер могут загружать разные файлы php.ini. Самый надёжный способ проверки — временная страница с phpinfo() на самом сайте. Удалите её сразу после просмотра.
Защитите свои PHP-сайты с Yeap CDN и WAF
PHP2Shell не будет последней критической уязвимостью PHP. Между публикацией бюллетеня и установкой патча на каждый сервер всегда есть окно, а злоумышленники обычно начинают сканирование в течение нескольких часов. Разместите сайт за Yeap CDN, и мы будем удерживать это окно за вас на границе сети:
- Управляемые правила, которые всегда актуальны: полный набор правил OWASP CRS v4 в 14 категориях, включая SQL-инъекции, XSS, удалённое выполнение команд, PHP-инъекции и веб-шеллы. Вы включаете и выключаете категории вместо настройки отдельных правил, а новые версии из upstream автоматически собираются, проходят канареечную проверку и развёртываются.
- Виртуальные патчи для критических уязвимостей: как и в этот раз, целевые правила включаются на границе в день выхода бюллетеня без каких-либо изменений на вашем источнике.
- Скрытый источник: злоумышленники могут добраться только до граничных узлов. Разрешите на источнике только трафик CDN, и поверхность прямой атаки сократится практически до нуля.
- Пользовательские правила и ограничение частоты запросов: комбинируйте IP, страну, путь, заголовок и метод, чтобы блокировать трафик, отправлять проверку или ограничивать частоту.
- Бесплатный HTTPS и глобальное ускорение: сертификаты выпускаются и продлеваются автоматически, а граничные узлы в нескольких регионах отвечают вашим посетителям из ближайшей точки.
Подключение занимает несколько минут: добавьте домен, направьте его DNS-запись на CNAME, который выдаст Yeap CDN, и включите защиту в настройках WAF домена. Пошаговое руководство см. в статье Подключите сайт к Yeap CDN с бесплатным HTTPS за 10 минут, а подробности о продуктах — на страницах Yeap CDN и Безопасность.
Мы включаем движок управляемых правил OWASP CRS поэтапно, домен за доменом. Если вы хотите получить этот виртуальный патч PHP2Shell для своего сайта прямо сейчас, создайте обращение в службу поддержки в консоли, и мы включим его для вашего домена.
Ссылки
Создавайте проекты на Yeap Cloud
Бесплатные тарифы DNS и CDN для каждого нового аккаунта.
Читать дальше
Новые пограничные узлы Yeap CDN: почему мы выбрали Rust
Мы заменили пограничный компонент CDN собственной реализацией на Rust и Pingora. Что это даёт вашему сайту: более быстрое подключение, применение конфигурации за секунды, усиленную изоляцию и безопасность.
Начало работы с Yeap LLM API через OpenAI SDK
Yeap LLM API совместим с OpenAI: используйте привычный SDK, указав https://llm.yeap.cloud/v1 в base_url. Создание ключа, примеры на Python и Node.js, потоковые ответы, эмбеддинги, квоты и ограничения моделей и IP для ключей.
Практическое руководство по очистке кэша: от ключей до отдельных вариантов
Очистили кэш, но по-прежнему получаете попадания? Чаще всего причина в ключе кэша. Как Yeap CDN формирует ключи, как работают режимы строки запроса и что именно удаляет очистка по URL с параметрами и без них.