PHP2Shell: как Luntry защищает уязвимые PHP-нагрузки, пока устанавливаются патчи
23 октября 2026

Коротко. PHP2Shell — это две критические уязвимости PHP (CVE-2026−109 618 и CVE-2026−109 619, обе CVSS 9.2), позволяющие неаутентифицированному злоумышленнику удалённо выполнить код на сервере (подробнее о ситуации пишет РБК). Они затрагивают практически все версии PHP за последнее десятилетие и все способы обслуживания web-запросов.


Единственное полное решение — обновление до исправленных версий. Пока патчи раскатываются по инфраструктуре, российское решение для защиты контейнеров Luntry позволяет обнаружить и остановить эксплуатацию, обеспечивая надежную безопасность контейнеров в России и защиту Kubernetes, не дожидаясь полного обновления.

Что такое уязвимости PHP2Shell?

21 октября 2026 года команда PHP выпустила экстренный набор обновлений безопасности, закрывающий две критические уязвимости, получившие общее имя PHP2Shell:
•     CVE-2026−109 618 (GHSA-w8hw-w9fx-j79w) — RCE через повреждение памяти при нехватке памяти (out-of-memory). Если запрос упирается в memory_limit в неудачный момент, PHP может оставить внутренние данные запроса частично инициализированными, и последующая очистка трактует их как валидные.

•     CVE-2026−109 619 (GHSA-6wmf-4rv5-p3rp) — RCE через устаревшее глобальное состояние. Фатальная ошибка в неудачный момент может оставить внутреннее состояние, указывающее на уже освобождённую память. Cледующий запрос в том же worker-процессе обращается к ней до штатной инициализации (use-after-free между запросами).

Обе уязвимости относятся к классу проблем безопасности памяти и связаны с тем, как PHP обрабатывает фатальные ошибки во время выполнения запроса. Общий знаменатель — прерванный запрос оставляет повреждённое или устаревшее внутреннее состояние, которое затем переиспользуется в том же долгоживущем worker-процессе. Обе оценены в 9.2 балла по CVSS v4.0.

Уязвимости найдены в рамках скоординированного раскрытия: одну обнаружила команда Calif в ходе аудита PHP по программе OpenAI «Patch the Planet», а компания Wiz независимо нашла и отправила обе. Исправления выпускала команда PHP при поддержке PHP Foundation. Технические подробности — в материалах PHP, Wiz, Calif и PHP Foundation.

Почему PHP2Shell требует немедленных действий?

Масштаб определяется распространённостью PHP: на нём работает порядка 70% сайтов, язык которых известен. Уязвимости затрагивают все способы обслуживания web-запросов — PHP-FPM, Apache с mod_php, php-cgi, FrankenPHP и LiteSpeed, причём наибольший риск, по оценке команды PHP, несёт mod_php, где неаутентифицированный запрос может сработать ещё до выполнения прикладного кода. По данным исследователей, затронута практически каждая версия PHP за последнее десятилетие, вплоть до 5.4, а уязвимо даже приложение уровня «Hello World» — проблема лежит ниже уровня прикладного кода.

Дополнительный фактор срочности — то, как быстро после раскрытия появляется работающий эксплойт. Уязвимости выявлены с помощью AI-ассистентов, а за день до планового раскрытия идентификаторы CVE и часть сведений преждевременно попали в публичный доступ. На момент выхода исправлений отдельные исследователи (в частности, watchTowr) сообщали о попытках эксплуатации «в дикой природе». Другие (Huntress) на тот же момент не фиксировали массовой эксплуатации в своей базе, а публично подтверждённых фактов компрометации серверов на момент раскрытия не приводилось. Тем не менее окно между раскрытием и установкой патчей — именно тот период, когда особенно важны средства защиты, не завязанные на сигнатуру конкретной CVE.

Затронут ли Luntry уязвимостью PHP2Shell?

Нет. Luntry не использует PHP ни в каком виде — ни напрямую, ни в составе компонентов. Соответственно, использование продуктов Luntry не могло поставить клиентов под угрозу, связанную с PHP2Shell. При этом Luntry помогает обнаруживать и сдерживать эксплуатацию этих уязвимостей в защищаемых контейнерных нагрузках клиента, пока идёт обновление.

Почему одного сканирования уязвимостей недостаточно?

Сканирование образов необходимо, но его одного мало. Уязвимая версия PHP может оказаться в кластере в обход проверок — через сторонний образ, быстрое развёртывание или уже работающую нагрузку, которую ещё не успели обновить. Патч может выйти с задержкой или потребовать согласованного окна обслуживания, а в крупных парках обновление растягивается на дни и недели.

Кроме того, PHP-приложения на платформе появляются из множества базовых образов, и неочевидно, какие именно из работающих нагрузок содержат уязвимую версию интерпретатора. Поэтому к сканированию необходимо добавить два уровня: инвентаризацию работающих нагрузок (чтобы понимать, что именно уязвимо прямо сейчас) и контроль поведения в Runtime (чтобы обнаружить и остановить эксплуатацию независимо от того, установлен ли уже патч).

Как мы воспроизводили PHP2Shell в Kubernetes

Чтобы показать, как эксплуатация выглядит со стороны защиты, мы воспроизвели её на стенде. За основу взяли публичный эксплойт php2shell-bypass-aslr-exp (https://github.com/9bie/php2shell-bypass-aslr-exp/tree/main), нацеленный на официальный образ php:8.4.26-apache (версия до исправления 8.4.27). Под docker-compose он отрабатывает «как есть», однако при прямом переносе в Kubernetes Pod эксплуатация не воспроизводилась.

Причина в том, что цепочка чувствительна к распределению памяти в процессе, а окружение Pod’а отличается от docker-compose. Детали самой эксплуатации мы намеренно опускаем — ниже только изменения окружения, которые потребовались для детерминированного воспроизведения на стенде (без них эксплойт просто не срабатывал). Правки затронули ConfigMap’ы и Deployment:
apiVersion: v1
kind: ConfigMap
metadata:
  name: php2shell-php-ini
  namespace: php2shell
  labels:
    app: php2shell-target
data:
  zz-php2shell.ini: |
    ; php2shell lab settings (mounted into the official php:8.4.26-apache image)
    ; These mirror the configuration used by the ASLR chain.
    display_errors = On
    display_startup_errors = On
    error_reporting = E_ALL

    memory_limit = 8M
    post_max_size = 64M
    upload_max_filesize = 64M

    ; Drop $_ENV (variables_order has no "E"): the official image defaults to
    ; EGPCS, so PHP otherwise rebuilds $_ENV from the process environment every
    ; request. In a pod that environment carries 8 injected KUBERNETES_* vars
    ; (docker-compose has none), and those extra entries shift the Zend MM
    ; allocation sequence non-uniformly -- not just the stage-3 carrier but the
    ; trigger groom too -- so the chain misses at any htdata. Dropping $_ENV
    ; makes the pod's per-request heap env-independent and identical to the
    ; docker-compose baseline the chain is calibrated on (htdata=0x690d8).
    variables_order = "GPCS"

    session.save_path = "/tmp"
    session.use_strict_mode = 0
    session.use_cookies = 1
    session.upload_progress.enabled = On
    session.upload_progress.cleanup = On
Окружение процесса (variables_order). В ini-ConfigMap задали variables_order="GPCS", отключив заполнение $_ENV. Официальный образ использует EGPCS и пересобирает $_ENV при каждом запросе; в Pod’е окружение несёт дополнительные переменные KUBERNETES_* (которых нет в docker-compose), и эти лишние записи сдвигают последовательность выделений в аллокаторе Zend так, что заранее откалиброванная цепочка промахивается. Отказ от $_ENV делает распределение памяти на запрос независимым от окружения и совпадающей с эталоном docker-compose.

Service-link переменные (enableServiceLinks). В Deployment выставили enableServiceLinks: false — по той же причине: чтобы Kubernetes не внедрял переменные вида <SERVICE>_*, которые тоже смещают последовательность выделений.

Один pod и один worker. Цепочка работает в пределах одного адресного пространства и опирается на ротацию единственного prefork-worker'а Apache. Поэтому replicas: 1 со стратегией Recreate, а в apache-ConfigMap — ServerLimit 1, MaxRequestWorkers 1 и KeepAlive Off. Второй Pod или второй worker — это другое адресное пространство, и воспроизведение ломается.

Без проб readiness/liveness. Health-пробы пришлось убрать: httpGet-проба обслуживается тем же единственным worker’ом и попадает между запросами эксплойта, запуская per-request инициализацию и завершение, которые возмущают состояние процесса, — и попытка промахивается. В docker-compose проб нет, поэтому там всё срабатывало, а под с пробами — нет.

Архитектура amd64. Смещения зависят от архитектуры, поэтому узел должен исполнять linux/amd64 (как и platform: linux/amd64 в docker-compose).
С этими изменениями стенд воспроизводится детерминированно, и запуск публичного эксплойта против него приводит к выполнению команды в контейнере (процесс запускается от www-data). Это и есть тот момент, который фиксирует поведенческая защита Luntry.
Показательно, что сама по себе хрупкость цепочки — она ломается от второго worker’а, health-пробы или нескольких лишних переменных окружения — это уже наблюдаемое с точки зрения защиты свойство: успешная эксплуатация требует очень специфичных условий и сопровождается аномальной активностью, на которую и опирается Runtime-контроль.

Как Luntry защищает PHP-нагрузки?

Runtime политики Luntry основаны не на сигнатурах конкретных атак, а на анализе поведения процессов и приложений во время выполнения. Легитимное PHP-приложение ведёт себя предсказуемо: worker-процесс (php-fpm или node/php в составе образа) обслуживает запросы и не порождает сторонних процессов.
Так выглядит наше тестовое приложение. В качестве заглушки, index. php отдает «Luntry PoC».
Успешная эксплуатация PHP2Shell, как и любого RCE, нарушает эту модель: в контейнере появляется новый процесс — shell или интерпретатор, запущенный из web-контекста, — за которым следуют разведка, закрепление и сетевые обращения к внешним адресам.

Благодаря поведенческому подходу такие отклонения фиксируются независимо от того, известна ли конкретная CVE на момент атаки. Другими словами, обнаружить эксплуатацию PHP2Shell можно было бы, даже если бы уязвимость ещё оставалась 0-day.
Во время установки патчей Luntry добавляет несколько независимых уровней защиты вокруг уязвимых PHP-нагрузок. Каждый из них работает по поведению, а не по сигнатуре уязвимости.

Детектирование по правилам. Помимо поведенческих аномалий, Runtime Rules Luntry срабатывают на типовую пост-эксплуатационную активность: порождение shell или интерпретатора процессом web-сервера, reverse-shell паттерны (/dev/tcp/, fsockopen, mkfifo), разведывательные команды (whoami, id, uname -a) и чтение переменных окружения. Встроенная библиотека правил покрывает матрицу MITRE ATT&CK для контейнерных угроз, при этом можно добавлять собственные детекты.
Сбор форензики и реагирование. Reaction Policies позволяют при срабатывании заданных условий (например, создание файла для закрепления или запуск исполняемого файла, которого не было в образе) автоматически собрать артефакты для расследования — дамп файловой системы и RAM — и остановить контейнер.
Терминал ниже показывает эту реакцию со стороны атакующего. Как только срабатывает Reaction Policy с действием Stop, контейнер с уязвимым PHP останавливается прямо в ходе эксплуатации: следующий запрос ротации worker’а упирается в уже остановленный контейнер — соединение отклоняется (Connection refused), и эксплойт аварийно завершается.
Как клиенты Luntry могут выявить уязвимые нагрузки?

Luntry поддерживает сканирование контейнерных образов в Runtime, CI и Registry, что позволяет выявлять уязвимые версии PHP как до деплоя, так и в уже работающих workload’ах. Результаты обогащаются внешними источниками — метрикой вероятности эксплуатации EPSS, информацией о наличии публичного эксплойта и сведениями о фактах эксплуатации «in-the-wild» из каталога CISA KEV. Для PHP2Shell такая приоритизация выводит уязвимые образы в верх списка независимо от прочих CVE.

Чтобы не допустить попадания уязвимых версий в продакшн, служат Security Gates: они контролируют соответствие приложений требованиям безопасности и блокируют деплой на этапе CI при их нарушении, а в Registry и Runtime помечают несоответствие и уведомляют. Достаточно задать условие «в образе обнаружена критическая уязвимость, доступная по сети и имеющая эксплойт», и сборка образа с уязвимой версией PHP будет остановлена ещё в пайплайне.

Что делать организациям прямо сейчас?

  1. Обновите PHP до исправленной версии в соответствии с вашей веткой (см. таблицу ниже). Это единственное полное решение.
  2. Проведите инвентаризацию: с помощью сканирования в Runtime и Registry определите, какие работающие нагрузки содержат уязвимую версию PHP, и приоритизируйте их по EPSS и CISA KEV.
  3. Включите Runtime-защиту для уязвимых нагрузок на время обновления: Prevention Policies на allow-list процессов, Runtime Rules и сгенерированные Network Policy.
  4. Настройте Security Gates, чтобы заблокировать повторное попадание уязвимых версий на этапе CI и отслеживать их в Registry и Runtime.
  5. Если немедленное обновление невозможно, примените временные частичные меры из официального advisory PHP (memory_limit=-1 и низкий post_max_size для CVE-2026−109 618; session. upload_progress.enabled=0 для CVE-2026−109 619, а на PHP 8.5 — дополнительно fatal_error_backtraces=0). Команда PHP подчёркивает, что эти меры лишь снижают поверхность атаки и не заменяют патч.
Таблица 1. Исправленные версии PHP, выпущенные 21 октября 2026 года
PHP2Shell показывает главное: одной информации об уязвимости недостаточно
Luntry объединяет сканирование уязвимостей, контроль безопасности на этапах CI/CD и Runtime-защиту контейнеров. Поведенческий анализ и правила позволяют обнаруживать и блокировать эксплуатацию даже тогда, когда конкретная уязвимость еще не известна системе защиты.
Хотите увидеть, как это работает в вашей инфраструктуре? Посмотреть все возможности платформы Luntry и запросить демо.
Выводы

PHP2Shell ещё раз подтверждает необходимость сочетать обнаружение уязвимостей и атак, своевременное обновление и контроль в Runtime. Сканирование показывает, что уязвимо; обновление устраняет причину; а Runtime-защита, сочетающая поведенческий анализ и правила, прикрывает нагрузки в самый опасный период — между раскрытием уязвимости и установкой патча — и остаётся рабочей, даже если уязвимость всё ещё 0-day. Runtime-контроль Luntry добавляет уровень защиты на время, пока идёт обновление.

FAQ