PHP2Shell CVE-2026-109618 и 109619: что делать за первый час
АйТи Фреш
Серверы и инфраструктура

PHP2Shell: CVE-2026-109618 и CVE-2026-109619 — что делать за первый час

Автор: , директор ООО «АйТи-Фреш» · · ~20 мин чтения
Серверная стойка с тревожной янтарной индикацией и треснувшим щитом на стене — уязвимость PHP2Shell в ядре PHP
PHP2Shell поражает сам интерпретатор PHP, поэтому защита приложения без обновления ядра не помогает.

21 октября 2026 опубликованы две критические уязвимости ядра PHP, CVE-2026-109618 и CVE-2026-109619 (CVSS v4.0 9.2): удалённое выполнение кода без входа в систему. Затронуты любые версии PHP, а не отдельные CMS. Исправления вышли в PHP 8.5.12, 8.4.27, 8.3.36 и 8.2.35; для 8.1 и старше официального патча нет. За первый час: найти весь PHP, обновить пакеты, перезапустить сервисы и включить временные меры там, где обновление невозможно.

PHP2Shell на 21 октября 2026: что известно и кого касается

Две уязвимости в ядре PHP, получившие общее имя PHP2Shell, опубликованы 21 октября 2026. Это CVE-2026-109618 и CVE-2026-109619, обе с оценкой CVSS v4.0 9.2 (Critical) по версии PHP. Атака идёт по сети, без аутентификации, на конфигурации по умолчанию. Исправленные релизы вышли 21 октября в 14:00 UTC: PHP 8.5.12, 8.4.27, 8.3.36 и 8.2.35. Раскрытие ускорили из-за утечки деталей 20 октября, поэтому патч, разборы и инструмент эксплуатации уже публичны.

Суть в двух словах. В первом случае нехватка памяти прерывает PHP в момент, когда внутренние данные запроса инициализированы лишь частично, а очистка запроса считает их валидными. Sansec описывает это как use-after-free в десериализаторе upload-progress. Следствия: утечка содержимого освобождённой памяти воркера (в заголовке Set-Cookie с PHPSESSID могут вернуться пароли, токены сессий, API-ключи, приватные ключи TLS, причём без вызова session_start()) и выполнение кода. Второй случай: состояние запроса переживает освобождение памяти, и следующий запрос на том же воркере использует устаревшее состояние. Sansec связывает это с обработчиком backtrace при фатальной ошибке и type confusion в OPcache. Итог тоже выполнение кода.

Запускает цепочку фатальная ошибка во время запроса, чаще всего исчерпание memory_limit. Все известные на сегодня триггеры используют запрос multipart/form-data: PHP разбирает его тело ещё до кода вашего приложения. Поэтому режим обслуживания в CMS, фильтры в плагинах и аккуратный код сайта ничего не меняют: запрос обрабатывается интерпретатором раньше. Это важное отличие от привычных историй про «дырявый плагин».

Статья горячая, картина меняется по часам. Что надо проверять заново в день, когда вы читаете текст: появились ли исправленные пакеты в вашем дистрибутиве (на момент проверки пакет для Debian 12 у одного из авторов обзоров был ещё в статусе «not yet»), вышли ли сборки Sury для 7.4 и 8.1 (подтверждения на 21 октября нет), и что пишут вендоры ваших панелей и продуктов. По 1С-Битрикс, ISPmanager, HestiaCP, cPanel, Plesk, mailcow, Zabbix, GLPI, FreePBX и Windows-сборкам PHP заявлений вендоров на момент проверки найти не удалось: на 21 октября не подтверждено, проверьте у вендора. По Patchstack, к 13:57 по телеметрии наблюдалось только зондирование без подтверждённых взломов, но Sansec ждёт массовой эксплуатации.

Уязвимый код присутствует с PHP 5.4.0 (2012 год). По разбору Wiz, все версии начиная с 5.4 затронуты хотя бы одной из двух уязвимостей, а NCSC Великобритании формулирует ещё шире: затронуты любые версии PHP. Уязвимы mod_php, PHP-FPM, php-cgi, FrankenPHP и LiteSpeed (по обзорам BleepingComputer и других изданий). Конфигурация по умолчанию тоже уязвима, то есть «мы ничего не трогали» не защита.

Раз ошибка в интерпретаторе, значит, уязвимо любое приложение, которое на нём работает. Sansec подтвердил выполнение кода без входа в систему на Magento 2.4.9, WordPress 7.1.2, Drupal 11.4.3, Joomla 6.1.4, MediaWiki 1.46.2, Roundcube 1.7.4, phpMyAdmin 5.2.3, Nextcloud 35.0.1, Laravel 13.11, Symfony 8.1 и WHMCS. Это актуальные версии приложений, и именно в этом смысл: обновление плагинов, тем и самой CMS до последней версии от PHP2Shell не защищает. Нужно обновлять PHP.

Что обычно забывают при подсчёте. Корпоративный сайт и интернет-магазин помнят все. Хуже с внутренними сервисами, которые «никому не видны», но доступны из офисной сети или через проброшенный порт: система учёта техники, мониторинг, веб-интерфейс телефонии, почтовый клиент, phpMyAdmin на сервере баз данных. NCSC прямо просит включать в инвентаризацию весь PHP, в том числе встроенный в продукты и аппаратные решения. Если вы поддерживаете такие системы сами, подробнее об учёте активов у нас в материале как провести инвентаризацию IT-активов.

Для групп систем мы разобрали отдельные статьи серии: Debian и Ubuntu: репозитории Sury и PPA, CentOS 7, RHEL, Alma и Rocky: Remi и EOL, панели с несколькими версиями PHP, 1С-Битрикс и BitrixVM, WordPress, Joomla, OpenCart, Docker-образы, Nextcloud, mailcow, Zabbix, GLPI, FreePBX, Windows, IIS и FastCGI. Эта статья головная: здесь порядок действий на первый час и общая таблица.

Серверы, на которых нет сайта, но стоит любой PHP-веб-интерфейс, в инвентаризации выпадают чаще всего. Проверяйте процессы, а не список сайтов.

Первый час: инвентаризация PHP на всех серверах (минуты 0–15)

Порядок такой, как мы строим его у клиентов: сначала узнать, где вообще есть PHP и какой версии, потом решать по каждому узлу. Нельзя начинать с обновления одного известного сайта: при пятнадцати узлах легко пропустить забытый тестовый сервер или контейнер. Для каждого сервера запишите: версию, способ запуска (mod_php, FPM, FrankenPHP, LiteSpeed), доступен ли он из интернета, где хранит секреты.

Первая команда показывает версии, установленные из пакетов. На Debian и Ubuntu:

dpkg -l | grep -E '^ii\s+php'
apt list '?installed ?name(^php[0-9.]+$)'

На RHEL, Alma, Rocky и CentOS:

rpm -qa | grep -i '^php'

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

ps -eo pid,user,args | grep -E 'php-fpm|apache2|httpd|lsphp|frankenphp|php-cgi' | grep -v grep
find / -xdev -type f \( -name 'php' -o -name 'php-fpm*' -o -name 'php-cgi' -o -name 'lsphp' \) 2>/dev/null

Для каждого найденного бинарника выполните его с ключом -v: у сервера может быть системный PHP одной версии, а сайт работает на версии из каталога панели или из каталога /opt. Контейнеры инвентаризируются отдельно, потому что внутри образа своя версия независимо от хоста:

docker ps --format '{{.Names}} {{.Image}}'
docker exec ИМЯ_КОНТЕЙНЕРА php -v

Отдельная строка в списке — внешний периметр. Определите, какие из найденных узлов принимают HTTP из интернета, какие только из офисной сети, а какие закрыты. Порядок обновления вытекает из этого: сначала доступные из интернета, потом внутренние, но внутренние без проверки не оставляем. Если вы уже ведёте реестр в системе учёта, вынесите в него столбец «версия PHP»: для этого подходят и Snipe-IT в Docker, и регламент эксплуатации Snipe-IT.

Мини-кейс. Возьмём условную транспортную компанию «Северный Путь» на 28 рабочих мест. Ситуация (описываем подход, а не результат): публичный сайт с формой заявки на перевозку, внутренний портал диспетчеров, система учёта техники и мониторинг на отдельных виртуальных машинах, плюс забытая тестовая копия сайта. Мы бы не стали сразу обновлять сайт. Сначала запускаем одну и ту же инвентаризацию на всех машинах, фиксируем версию и способ запуска, отмечаем, какие узлы видны из интернета, и только потом выстраиваем порядок: публичный сайт и тестовая копия первыми, внутренние сервисы следом, система учёта и мониторинг после проверки их версии у вендора. Тестовую копию, если она не нужна, лучше отключить совсем: она увеличивает поверхность атаки и ничего не даёт.

Команда find по всей файловой системе нагружает диск. На рабочем сервере запускайте её вне пиковых часов или ограничьте каталогами /usr, /opt и /var.
Схема первого часа: инвентаризация серверов, лупа, значки проверки и шестерёнка обновления
Порядок первого часа: найти весь PHP, решить по каждому узлу, обновить и проверить.

Таблица: версия PHP, статус, что делать

Статус привязан к данным на 21 октября 2026. Исправленные версии upstream: 8.5.12, 8.4.27, 8.3.36 и 8.2.35. Для PHP 8.1 и старше официального исправления нет, потому что ветки закрыты. Patchstack отмечает, что минимальные бэкпорты для 7.4, 8.0 и 8.1 существуют, но только для сборки из исходников.

Версия PHPСтатус на 21.10.2026Что делать
8.5 (до 8.5.12)Исправление есть: 8.5.12Обновить пакет, перезапустить сервисы, для 8.5 при временных мерах добавить fatal_error_backtraces=0
8.4 (до 8.4.27)Исправление есть: 8.4.27Обновить пакет или пересобрать образ, перезапустить
8.3 (до 8.3.36)Исправление есть: 8.3.36Обновить пакет, перезапустить PHP-FPM или Apache
8.2 (до 8.2.35)Исправление есть: 8.2.35Обновить пакет, перезапустить
8.1Исправления upstream нет, ветка закрытаПерейти на поддерживаемую ветку; пока не перешли, пакет дистрибутива (Ubuntu 22.04 имеет свой php8.1) и временные меры
8.0, 7.4 и старшеИсправления upstream нетСрочно планировать миграцию; пакет дистрибутива, если он есть, иначе временные меры и изоляция
5.x и 7.x на EOL-системахОфициальных обновлений не ожидается (вывод: EOL-системы их не получают)Вынести из интернета, закрыть доступ, планировать замену

Для систем с пакетами дистрибутива смотрите версию пакета, а не номер upstream. Ubuntu выпустила исправления (USN-8948-1 и USN-8948-2): 22.04 — php8.1 8.1.2-1ubuntu2.28; 24.04 — php8.3 8.3.6-0ubuntu0.24.04.13; 26.04 — php8.5 8.5.4-0ubuntu1.5; 26.10 — php8.5 8.5.9-0ubuntu2.1. Для 20.04 (php7.4 7.4.3-4ubuntu2.29+esm5), 18.04, 16.04 и 14.04 исправления доступны только по Ubuntu Pro (ESM): sudo pro fix CVE-2026-109618. Номер пакета с суффиксом ubuntu при этом ниже upstream, и это нормально: сравнивать нужно с версией из бюллетеня.

ДистрибутивПакет с исправлениемПримечание
Debian 138.4.27-1~deb13u1 (DSA-6574-1)Проверить через apt-cache policy
Debian 128.2.35-1~deb12u1 (DLA-4848-1)На момент проверки у одного из обзоров пакет значился как «not yet»
Debian 11Исправления нетLTS закончился 31 августа 2026
Ubuntu 22.04 и 24.04php8.1 8.1.2-1ubuntu2.28, php8.3 8.3.6-0ubuntu0.24.04.13Обычное обновление
Ubuntu 20.04 и старшеТолько через Ubuntu Pro (ESM)sudo pro fix CVE-2026-109618
RHEL, Alma, RockyВерсии пакетов в источниках не названыСмотреть dnf updateinfo, Remi 21 октября обновлений не выпускает
CentOS 7EOLВывод: официальных обновлений не ожидается

Особый случай — Debian и Ubuntu с репозиторием Sury. Ондржей Сури заявил, что backport будет для каждой версии PHP в его репозитории (5.6–8.1) на всех поддерживаемых Debian и Ubuntu. Пакеты 8.2.35, 8.3.36, 8.4.27 и 8.5.12 уже вышли, для 7.4 и 8.1 подтверждения на момент проверки нет. Поэтому для старых веток проверяйте apt-cache policy php7.4-fpm и сравнивайте версию с changelog. Подробности — в статье обновление PHP из Sury и PPA на Debian и Ubuntu.

Не доверяйте строке php -v как единственному свидетельству. Дистрибутивы выпускают исправление под старым номером версии: смотрите номер пакета и бюллетень безопасности.

Обновление и перезапуск: минуты 15–40

Обновить PHP — это два действия: поставить пакет и перезапустить процессы. Вторую часть забывают чаще всего, и сервер остаётся уязвимым при «зелёных» логах обновления. Unattended-upgrades сам не перезапускает PHP-сервисы, а процессы, запущенные до обновления, продолжают выполнять старый код из памяти.

Debian и Ubuntu:

sudo apt update && sudo apt install --only-upgrade '?name(^php[0-9.]+$)'
sudo systemctl restart php8.3-fpm

Имя сервиса зависит от ветки: php8.2-fpm, php8.3-fpm, php8.4-fpm. Для PHP-FPM подходит и reload, но мы берём restart, чтобы не гадать. Если сайт работает через mod_php, нужен именно systemctl restart apache2: reload и graceful недостаточны. LiteSpeed: обновить lsphpXY и выполнить pkill lsphp. FrankenPHP не лечится через apt или dnf, нужен новый релиз или образ. Для очередей Laravel выполните php artisan queue:restart; для Octane нужен перезапуск процесса, octane:reload недостаточно.

На RHEL-совместимых системах версии исправленных пакетов в источниках не названы, поэтому сначала смотрим, что предлагает дистрибутив:

dnf updateinfo list --security | grep php
rpm -q --changelog php-fpm | head

Если ваша версия PHP — модуль из AppStream, при необходимости перейти на поддерживаемую ветку используется переключение модуля:

dnf module reset -y php && dnf module enable -y php:8.2 && dnf distro-sync -y

Перед такой командой проверьте, что приложение запускается на выбранной ветке: переход с 7.4 на 8.2 ломает код с устаревшими функциями. Remi 21 октября обновлений для этого случая не выпускает и рекомендует пакеты дистрибутива или сборку из исходников; разбор этого сценария и работа с EOL — в статье PHP2Shell на CentOS 7, RHEL, Alma и Rocky.

Docker. Образ docker-library/php обновлён в репозитории, но на Docker Hub он появляется после отдельного запроса в official-images и сборки. Теги 8.5.12RC1 и 8.4.27RC1 исправления не содержат. Свои образы пересобирайте с ключом --pull и пересоздавайте контейнеры, иначе работает старый слой:

docker build --pull -t myapp:php-fixed .
docker run --rm php:8.4-fpm php -v

Что делать с панелями, где несколько версий PHP на одном сервере, в чём разница между системным PHP и версией сайта, разобрано в статье HestiaCP и ISPmanager с несколькими версиями PHP. Для 1С-Битрикс и BitrixVM смотрите отдельный материал по Битриксу: статус вендора на 21 октября не подтверждён, проверьте у вендора.

Обновляем по одному узлу и после каждого проверяем сайт и формы загрузки. Массовое обновление парка одной командой без проверки мы не делаем.

Если обновить нельзя прямо сейчас: временные меры

Меры из этого раздела частичные и обновление не заменяют. Gina Peter Banyard из команды PHP прямо пишет: одного отключения upload-progress мало, надёжных обходных путей по сути нет, единственное исправление — патч. Мы используем их, чтобы закрыть окно между «узнали» и «обновили», и не больше.

Первая мера: отключить session.upload_progress.enabled=0 в php.ini, в конфигурации пула FPM или сервера. Не через ini_set() и не через .user.ini. После этого нужно перезапустить все воркеры. Для PHP 8.5 дополнительно задаётся fatal_error_backtraces=0. Вторая мера по Sansec: memory_limit = -1. Риск понятен: один запрос может съесть всю память сервера, поэтому следите за RAM. В пулах FPM:

php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1

Значения прописываются в каждом пуле через php_admin_*, чтобы приложение не могло их переопределить, затем reload. Patchstack для CVE-2026-109618 добавляет: post_max_size 8M при отсутствии загрузок и 32M при наличии, upload_max_filesize ниже, лимиты размера запроса, числа полей и multipart на веб-сервере или балансировщике, ограничения контейнеров и cgroup. Использовать RLIMIT_AS и RLIMIT_DATA не нужно. Для CVE-2026-109619: убрать настройки, которые создают диагностику до инициализации, и чинить доступные атакующему фатальные ошибки и таймауты.

Более жёсткая мера — запретить multipart/form-data на веб-сервере (вариант Sansec). Для nginx:

if ($http_content_type ~* "multipart/form-data") { return 403; }

Для Apache используется условие по заголовку Content-Type с отказом:

RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]

Цена такой меры: ломаются загрузки файлов и часть форм, включая корзину и оформление заказа. Обязательно проверьте тестовый заказ и форму заявки, прежде чем оставить правило. Ещё более грубый вариант — пропускать только GET и HEAD (в nginx отвечать 503 на остальные методы) или остановить PHP-FPM и отдавать статическую страницу. Режимы обслуживания CMS (Magento, WordPress) не помогают, потому что PHP разбирает запрос раньше.

Cloudflare выкатил аварийные WAF-правила для всех тарифов, включая бесплатный. Это тоже лишь временная мера, и работает она только если ваш сервер принимает трафик исключительно от Cloudflare. Sansec Shield выполнение кода и утечку не закрывает. Не ставьте «плагины-заплатки» и сниппеты с форумов: Patchstack отдельно предупреждает об этом. По CMS у нас есть материал WordPress, Joomla и OpenCart под PHP2Shell.

Правило из NCSC: если обновить нельзя и меры слишком хрупки, а система не критична, рассмотрите её отключение. Перед откладыванием проверьте её на следы компрометации.

Проверка, признаки взлома и чек-лист

После обновления проверяем не пакет, а процессы. Первая проверка показывает, что запущенные процессы не используют удалённые библиотеки (пустой вывод означает, что всё в порядке):

sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps
sudo needrestart -b -r l

Вторая проверка: версия в changelog. На Ubuntu это обычно видно по номеру CVE, на Debian и Sury changelog может быть без номеров, тогда сравниваем версию с бюллетенем:

zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-fpm/changelog.Debian.gz

Признаки эксплуатации по Patchstack (они не доказывают ни взлом, ни его отсутствие): необычные multipart-запросы с последующей ошибкой memory_limit или повторяющимися HTTP 500, аномально большие или битые куки PHPSESSID, необъяснимые перезапуски воркеров PHP. Как искать это в журналах nginx и Apache, написано в статье признаки взлома PHP2Shell в логах nginx и Apache.

При признаках действуем так. Обновляем PHP и считаем сервер возможно скомпрометированным. Составляем перечень секретов, которые на нём хранились: ключи API, пароли баз данных, ключи TLS, токены. Этот перечень мы передаём владельцу на ротацию; сами пароли живых учётных записей мы не меняем, решение о смене принимает владелец. Для утечки через память это принципиально: ключи могли уйти до обновления, и патч сам по себе их не отзывает.

Чек-лист на первый час, который мы используем как основу:

Чек-лист на первый час (ядро закрыто, когда PHP обновлён до исправленной версии или пакета дистрибутива и перезапущены все процессы, включая mod_php, LiteSpeed, FrankenPHP и очереди; иначе должны стоять временные меры с записанным сроком):

Ошибка, которую мы видим чаще всего: «обновили плагины и CMS, значит защитились». От PHP2Shell это не помогает, уязвимость в интерпретаторе. Вторая ошибка: обновили пакет, но не перезапустили сервис.
Сравнение до и после: открытый замок и утечка памяти слева, закрытый замок в щите и панель мониторинга справа
До обновления воркеры PHP могут отдавать содержимое памяти, после обновления и проверки риск закрыт.

Что делать после первого часа

Первый час закрывает самые опасные узлы, но не работу целиком. В ближайшие дни: повторить инвентаризацию через сутки (за это время вендоры публикуют обновления продуктов, и картина по Bitrix, панелям и веб-интерфейсам меняется), проверить репозитории для старых веток и вернуть к обновлению те узлы, где ждали пакет. Статус по Zabbix, GLPI и FreePBX мы разбираем отдельно: PHP-веб-интерфейсы Zabbix, GLPI и FreePBX; для Windows-серверов с IIS — PHP и FastCGI на Windows. Если у вас учёт техники и заявок в GLPI, посмотрите и GLPI: учёт техники и заявки пользователей.

Системные выводы. Любой сервер на PHP 8.1 и старше стоит в плане миграции: такие ветки не получили исправления upstream и после следующей громкой уязвимости история повторится. Реестр версий PHP должен жить постоянно, а не собираться в день аварии. Внешние сервисы должны быть закрыты по IP либо стоять за единой точкой входа, чтобы быстрые меры включались в одном месте.

Если у вас нет времени или специалиста, мы берём это на себя: инвентаризация, обновление, временные меры, проверка и отчёт владельцу. Описание формата работ — на странице обслуживание серверов и сайтов.

Частые вопросы

Что такое PHP2Shell и чем он опасен?

PHP2Shell — общее имя двух уязвимостей ядра PHP, CVE-2026-109618 и CVE-2026-109619, с оценкой CVSS v4.0 9.2. Они позволяют без входа в систему вызвать утечку памяти воркера и выполнение кода. Триггер — фатальная ошибка, обычно исчерпание memory_limit, при запросе multipart/form-data. Конфигурация по умолчанию уязвима.

Защитит ли обновление WordPress, Битрикса или плагинов?

Нет, обновление приложения не защищает. Ошибка находится в интерпретаторе PHP, который разбирает запрос multipart/form-data раньше кода сайта. Sansec подтвердил выполнение кода без логина на актуальных версиях WordPress, Joomla, Drupal, Magento и других. Нужно обновить сам PHP и перезапустить процессы, режим обслуживания CMS тоже не помогает.

Какие версии PHP исправлены?

Upstream исправлены PHP 8.5.12, 8.4.27, 8.3.36 и 8.2.35, релиз 21 октября 2026. Для PHP 8.1 и старше официального исправления нет, ветки закрыты. Дистрибутивы выпускают свои пакеты: Ubuntu — USN-8948-1 и USN-8948-2, Debian 12 и 13 — DLA-4848-1 и DSA-6574-1. Сравнивайте версию пакета с бюллетенем.

Что делать, если обновить PHP сейчас нельзя?

Включите временные меры, понимая, что они частичные. Отключите session.upload_progress.enabled в конфигурации (не через ini_set), для 8.5 добавьте fatal_error_backtraces=0, задайте лимиты запросов или заблокируйте multipart/form-data на веб-сервере, предварительно проверив формы. Если система некритична и мер мало, рассмотрите её отключение.

Как понять, что сервер уже взломали?

Достоверного признака нет. Patchstack называет косвенные: необычные multipart-запросы с последующей ошибкой memory_limit или повторяющимися HTTP 500, аномально большие или битые куки PHPSESSID, необъяснимые перезапуски воркеров PHP. При подозрении обновите PHP, считайте сервер возможно скомпрометированным и передайте владельцу список секретов для ротации.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи