Блог Wertigo

Уязвимость PHP2Shell в PHP: как проверить и защитить сайт

Объёмные синие буквы PHP на сервере, через них идёт оранжевая трещина, которую закрывают бирюзовой пластиной и гаечным ключом, рядом щит

Что произошло

21 октября 2026 года разработчики PHP выпустили внеочередные версии 8.5.12, 8.4.27, 8.3.36 и 8.2.35. Они закрывают две критические уязвимости в самом интерпретаторе: CVE-2026-109618 и CVE-2026-109619. Исследователи назвали их PHP2Shell. Через них можно выполнить свой код на сервере без логина и пароля, отправив сайту специально подготовленные запросы.

Ошибка находится в PHP, а не в коде сайта. Поэтому под угрозой оказывается любой сайт на уязвимой версии: WordPress, Битрикс, самописный проект и даже страница из одной строки. Обновление CMS, тем и плагинов уязвимость не закрывает. Помогает только исправленная сборка PHP и перезапуск процессов, которые его выполняют.

  1. Узнайте версию. Посмотрите, какая сборка PHP обслуживает сайт, а не только что выводит команда в терминале.
  2. Обновите PHP. Установите исправленный выпуск своей ветки или пакет дистрибутива с исправлением.
  3. Перезапустите процессы. Без перезапуска PHP-FPM или Apache сайт продолжит работать на старом коде.
  4. Проверьте журналы. Ищите следы атак с 20 октября и смените пароли и ключи, если они найдутся.

Сведения об уязвимостях утекли до официальной даты публикации, и по данным Cloudflare атаки на сайты уже идут. Откладывать обновление не стоит.

Какие версии PHP уязвимы

В официальном уведомлении PHP перечислены поддерживаемые ветки. Уязвимы все выпуски ниже исправленного номера:

Ветка PHPУязвимые версииИсправлено в
8.5ниже 8.5.128.5.12
8.4ниже 8.4.278.4.27
8.3ниже 8.3.368.3.36
8.2ниже 8.2.358.2.35
8.1, 8.0, 7.4всеОфициальных выпусков нет, только исходные патчи для самостоятельной сборки
7.3 и старшепо данным Cloudflare, все начиная с 5.4Исправлений от PHP нет

Тестовая версия PHP 8.6.0 RC4 тоже содержит исправления, более ранние тестовые сборки 8.6 уязвимы. Для старых веток команда PHP из-за серьёзности проблемы подготовила минимальные патчи к исходному коду PHP 7.4, 8.0 и 8.1. Готовых сборок для этих веток PHP не выпускает, но их выпускают некоторые дистрибутивы Linux. Об этом ниже.

Ветка 8.2 перестанет получать исправления безопасности 31 декабря 2026 года. Если сайт работает на ней, после срочного обновления запланируйте переход на 8.3 или новее.

Чем опасна уязвимость и кого она касается

Обе ошибки связаны с тем, как PHP убирает за собой после сбоя. Когда обработка запроса обрывается из-за нехватки памяти или фатальной ошибки, часть внутренних данных остаётся недоделанной или указывает на уже освобождённую память. Злоумышленник подбирает запросы так, чтобы вызвать такой сбой, а затем подсовывает PHP свои данные вместо испорченных.

УязвимостьЧто ломаетсяГде подтверждена атака
CVE-2026-109618PHP заканчивает память при разборе отправленной формы с файлами и завершает запрос с наполовину заполненными даннымиmod_php: атака срабатывает ещё до запуска кода сайта
CVE-2026-109619После сбоя один запрос оставляет ссылки на освобождённую память, и следующий запрос к тому же процессу ими пользуетсяmod_php и PHP-FPM

Обе получили 9,2 балла из 10 по шкале CVSS 4.0. Разработчики PHP относят к группе риска все установки, которые обрабатывают запросы к сайтам: PHP-FPM, модуль Apache mod_php, php-cgi, FrankenPHP и LiteSpeed. Сильнее всего рискует mod_php, а работа через PHP-FPM атаку усложняет, но не исключает.

Исследователи на сайте PHP2Shell показали захват свежих установок WordPress, Drupal и Joomla без входа в админку. Кроме выполнения команд, ошибка позволяет прочитать память процесса PHP. В ней бывают пароли от базы данных, токены входа, ключи API и сертификаты, причём не только ваши, но и данные чужих запросов, которые обрабатывал тот же процесс.

Сайт на обычном хостинге

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

Свой сервер или VPS

Обновлять PHP придётся самостоятельно или поручить администратору. Обновление сайта через админку CMS не трогает PHP на сервере.

Сайт в контейнере

PHP находится внутри образа Docker. Нужно пересобрать образ на исправленной версии и пересоздать контейнеры.

Облачный сервис или конструктор

Если сайт работает в облачном сервисе и доступа к серверу у вас нет, PHP обновляет сам сервис. Следите за его новостями о сроках.

Как проверить, исправлен ли PHP на сервере

Просто сравнить номер версии с таблицей выше получится не всегда. У одного сервера бывает несколько установленных версий PHP, а дистрибутивы Linux часто исправляют уязвимость, не меняя номер. Поэтому сначала выясните, какой PHP обслуживает сайт.

Версия, на которой работает сайт

На хостинге номер версии виден в панели управления, обычно в разделе настроек PHP или сайта. В WordPress откройте «Инструменты» → «Здоровье сайта» → «Информация» → «Сервер»: там указана версия PHP. Универсальный способ — временный файл с функцией phpinfo. Создайте его в папке сайта:

info.php
<?php phpinfo();

Откройте https://ваш-сайт/info.php и посмотрите две строки. PHP Version показывает номер версии, Server API показывает способ запуска: «Apache 2.0 Handler» означает mod_php, самый рискованный вариант, «FPM/FastCGI» означает PHP-FPM. Сразу после проверки удалите файл: phpinfo показывает пути и настройки сервера, которые пригодятся взломщику.

Команда php -v в терминале показывает версию PHP для командной строки. Сайт может работать на другой версии, установленной рядом, поэтому сверяйтесь с тем, что показывает сам сайт или пакет, из которого запущен PHP-FPM или Apache.

Почему исправленный PHP может показывать старый номер

Debian и Ubuntu не переходят на новый выпуск PHP, а переносят исправление в свою версию. Такой перенос называется бэкпортом. Например, в Ubuntu 24.04 после обновления php -v по-прежнему показывает 8.3.6, хотя пакет уже защищён. Проверять нужно номер пакета. Исправленные пакеты на 21 октября 2026 года по данным трекеров Debian и Ubuntu:

СистемаПакетИсправленная версия пакета
Ubuntu 26.10php8.58.5.9-0ubuntu2.1
Ubuntu 26.04 LTSphp8.58.5.4-0ubuntu1.5
Ubuntu 24.04 LTSphp8.38.3.6-0ubuntu0.24.04.13
Ubuntu 22.04 LTSphp8.18.1.2-1ubuntu2.28
Ubuntu 20.04 LTSphp7.47.4.3-4ubuntu2.29+esm5, только с подпиской Ubuntu Pro
Debian 13 trixiephp8.48.4.27-1~deb13u1
Debian 12 bookwormphp8.28.2.35-1~deb12u1

В Red Hat, AlmaLinux, Rocky Linux и Fedora статус ищите в трекере Red Hat. В репозитории Remi пакеты повторяют номера PHP, поэтому для них подходит основная таблица выше.

Скрипт для проверки сервера

Чтобы не собирать сведения вручную, запустите на сервере скрипт ниже. Он только читает данные и ничего не меняет, это видно по коду. Скрипт показывает версию PHP, установленные пакеты и упоминание исправления в их журнале изменений, время запуска процессов PHP-FPM и Apache и временные меры защиты, которые уже записаны в настройках.

php2shell-check.sh
#!/bin/sh
# Проверка PHP после PHP2Shell (CVE-2026-109618, CVE-2026-109619).
# Скрипт только читает сведения о системе и ничего не меняет.

echo "== PHP в командной строке"
if command -v php >/dev/null 2>&1; then
  php -r '
    $fixed = ["8.2" => "8.2.35", "8.3" => "8.3.36", "8.4" => "8.4.27", "8.5" => "8.5.12"];
    $branch = PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;
    echo "Версия: ", PHP_VERSION, "\n";
    if (isset($fixed[$branch])) {
      echo version_compare(PHP_VERSION, $fixed[$branch], ">=")
        ? "Номер исправленный\n"
        : "Номер ниже " . $fixed[$branch] . ": для сборки с php.net это уязвимая версия, для пакета дистрибутива сверьте версию пакета\n";
    } elseif (version_compare($branch, "8.2", "<")) {
      echo "Ветка без официальных обновлений: нужен патч поставщика или переход на 8.2+\n";
    }
  '
else
  echo "Команда php не найдена"
fi

echo
echo "== Пакеты PHP"
if command -v dpkg-query >/dev/null 2>&1; then
  pkgs=$(dpkg-query -W -f='${Package} ${Version}\n' 2>/dev/null | grep -E '^(php[0-9.]*-(common|fpm|cgi|cli)|libapache2-mod-php[0-9.]*) ')
  fixed=$(zgrep -l 'CVE-2026-10961[89]' /usr/share/doc/php*-common/changelog.Debian.gz 2>/dev/null)
elif command -v rpm >/dev/null 2>&1; then
  pkgs=$(rpm -qa --qf '%{NAME} %{VERSION}-%{RELEASE}\n' 'php*')
  fixed=$(for pkg in $(rpm -qa 'php*common*'); do rpm -q --changelog "$pkg" | grep -q 'CVE-2026-10961[89]' && echo "$pkg"; done)
elif command -v apk >/dev/null 2>&1; then
  pkgs=$(apk info -v 2>/dev/null | grep -E '^php[0-9]*-(common|fpm|cli|apache2)-[0-9]')
fi
echo "${pkgs:-Пакеты PHP не найдены}"
[ -n "$fixed" ] && echo "$fixed" | sed 's/^/Исправление упомянуто в журнале: /'

echo
echo "== Запущенные процессы, которые выполняют PHP"
echo "Процесс, запущенный раньше обновления, работает со старым кодом: перезапустите его"
procs=$(ps -eo lstart=,args= 2>/dev/null || ps -o args 2>/dev/null)
procs=$(echo "$procs" | grep -E 'php-fpm: master|apache2|httpd|frankenphp|litespeed|lshttpd' | grep -v grep)
echo "${procs:-Не найдено}"

echo
echo "== Временные меры в файлах настроек"
conf=$(grep -RHsE \
  '^[[:space:]]*(php_admin_value|php_value|php_admin_flag|php_flag)?[[:space:][]*(session\.upload_progress\.enabled|fatal_error_backtraces|memory_limit|post_max_size)' \
  /etc/php* /etc/opt/remi /usr/local/etc/php* /etc/apache2 /etc/httpd 2>/dev/null | grep -v 'php\.ini-')
echo "${conf:-Настройки не найдены}"

Сохраните код в файл и запустите обычной командой. Для сайта в контейнере передайте скрипт внутрь контейнера:

Терминал
sh php2shell-check.sh

# сайт в контейнере
docker exec -i имя-контейнера sh -s < php2shell-check.sh

Строка «Исправление упомянуто в журнале» означает, что установленный пакет Debian или Ubuntu содержит патч, даже если номер PHP старый. Если время запуска процесса раньше времени обновления пакетов, процесс работает со старым кодом и его нужно перезапустить. Утилиты, которые сравнивают только номер версии из заголовков сайта или php -v, на пакетах дистрибутивов ошибаются: исправленный сервер они считают уязвимым.

Как обновить PHP

Устанавливайте исправленный выпуск той ветки, на которой уже работает сайт: 8.3 на 8.3, 8.4 на 8.4. Переход на новую ветку меняет поведение языка и может сломать сайт, его лучше делать отдельно и после проверки. Перед обновлением сделайте резервную копию сайта, базы данных и настроек сервера.

Debian и Ubuntu

Обновите пакеты, затем перезапустите службу, которая выполняет PHP. Имя службы PHP-FPM содержит номер ветки, например php8.3-fpm.

Терминал
sudo apt update
sudo apt upgrade

# PHP-FPM: подставьте свою ветку
sudo systemctl restart php8.3-fpm
# Apache с mod_php
sudo systemctl restart apache2

Если PHP установлен из стороннего репозитория, например deb.sury.org, команды те же: исправленные пакеты придут из него.

Red Hat, AlmaLinux, Rocky Linux, Fedora

Терминал
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm
# если используется Apache
sudo systemctl restart httpd

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

Docker

Официальные образы php обновляются по тегам вроде php:8.3-fpm. Если в Dockerfile указан точный номер, например php:8.3.30-fpm, замените его на исправленный. Затем пересоберите образ со свежей базой и пересоздайте контейнеры:

Терминал
docker compose build --pull
docker compose up -d

Windows

Скачайте исправленную сборку со страницы загрузок PHP, замените файлы PHP, сохранив свой php.ini, и перезапустите веб-сервер.

Хостинг

Если в панели можно выбрать сборку PHP, выберите исправленную версию своей ветки. Если такой возможности нет, напишите в поддержку и попросите подтвердить, что PHP обновлён и все процессы перезапущены.

После обновления обязательно перезапустите PHP-FPM или Apache: уже запущенные процессы продолжают выполнять старый код PHP. Перезапуск одного nginx процессы PHP-FPM не обновляет.

Если сайт работает на PHP 7.4, 8.0 или 8.1 и пакета с исправлением для вашей системы нет, остаются три пути: исправления от поставщика дистрибутива, например Ubuntu Pro или Debian LTS, самостоятельная сборка с патчами PHP или переход на поддерживаемую ветку. Последний вариант дольше, но избавляет от той же проблемы при следующей уязвимости.

Если обновить сразу нельзя

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

php.ini
; Временные меры до обновления, они не заменяют обновление
session.upload_progress.enabled = 0
; только для PHP 8.5
fatal_error_backtraces = 0
; 32M, если сайт принимает файлы
post_max_size = 8M
upload_max_filesize = 2M
  • session.upload_progress.enabled отключает отслеживание загрузки файлов, через которое подтверждена одна из атак. Эта функция почти нигде не используется, и разработчики советуют держать её выключенной даже после обновления.
  • fatal_error_backtraces нужен только в PHP 8.5: он убирает путь атаки, на котором построен самый надёжный из показанных эксплойтов.
  • post_max_size ограничивает размер отправленной формы. Разработчики советуют 8M для сайтов без загрузки файлов и 32M, если загрузка нужна. upload_max_filesize должен быть меньше.

Задавайте эти значения в php.ini, настройках пула PHP-FPM через php_admin_value и php_admin_flag или в конфигурации Apache, а затем перезапустите все процессы PHP. Вызов ini_set() в коде и файлы .user.ini не помогают: они срабатывают позже, чем атака.

Ещё несколько мер для администраторов:

  • memory_limit = -1 убирает лимит памяти PHP, на исчерпании которого построена первая атака. Взамен ограничьте память процессов средствами системы через cgroup или лимиты контейнера, иначе один запрос сможет занять всю память сервера. Лимиты RLIMIT_AS и RLIMIT_DATA использовать нельзя: они сами вызывают уязвимый сбой.
  • Если сайт не принимает формы с файлами, блокируйте такие запросы на веб-сервере или прокси до того, как они дойдут до PHP.
  • pm.max_requests = 1 в PHP-FPM заставляет каждый процесс обработать один запрос и завершиться. Это мешает второй атаке, но заметно снижает скорость сайта.
  • WAF помогает выиграть время. Cloudflare включил правила против PHP2Shell для всех тарифов с WAF, включая бесплатный, но сам называет их дополнительной защитой, а не заменой обновлению.

Скрывать ошибки через display_errors, error_reporting или символ @ бесполезно: уязвимый код срабатывает раньше, чем PHP решает, показывать ли сообщение.

Как понять, не взломали ли сайт

Проверьте журналы веб-сервера, PHP-FPM и системы начиная с 20 октября. Признаки, на которые стоит обратить внимание:

  • процесс веб-сервера запускает командную оболочку, curl, wget или другие программы, которые сайту не нужны;
  • сообщения о нехватке памяти Allowed memory size … exhausted на запросах с отправкой форм;
  • необъяснимые падения процессов PHP, например записи PHP-FPM о завершении дочернего процесса по сигналу;
  • повторяющиеся фатальные ошибки и Maximum execution time exceeded на одних и тех же адресах;
  • новые или изменённые PHP-файлы в папках сайта, которых вы не загружали.

Чистые журналы не доказывают, что взлома не было: разработчики PHP предупреждают, что сбой может произойти раньше, чем PHP запишет сообщение. Если признаки нашлись, обновите PHP, восстановите сайт из копии, сделанной до появления признаков, и смените все секреты, которые хранились на сервере: пароли базы данных и админки, ключи API, почтовые и платёжные токены. Новые пароли удобно создать в генераторе паролей. О том, каким должен быть пароль, рассказано в статье «Как придумать надёжный пароль».

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

Нужно ли что-то делать посетителям сайтов?

Нет. Уязвимость находится на сервере, через браузер она посетителю не вредит. Риск в другом: если взломанный сайт хранил ваши данные, они могли утечь. Если сервис сообщит о взломе, смените пароль от него и везде, где использовали такой же.

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

Скрипты в командной строке не принимают запросы из сети, поэтому описанные атаки к ним не относятся. Встроенный сервер для разработки php -S в списке разработчиков PHP не упомянут, но он тоже обрабатывает запросы: не открывайте его для доступа из сети и обновите PHP при следующей возможности.

Можно ли сразу перейти на PHP 8.4 или 8.5?

Можно, но не вместо срочного обновления. Смена ветки меняет поведение языка, и старые темы, плагины или модули могут перестать работать. Сначала установите исправленный выпуск текущей ветки, а переход проверьте на копии сайта. Ветка 8.2 перестанет получать исправления 31 декабря 2026 года, поэтому с ней переход стоит запланировать в ближайшее время.

Что делать, если хостинг ещё не обновил PHP?

Напишите в поддержку и спросите, когда будет установлена исправленная сборка и перезапущены процессы PHP. Если в панели уже доступна исправленная версия вашей ветки, переключитесь на неё сами. Если сайт хранит важные данные, а сроков нет, на время закройте его режимом обслуживания или паролем на уровне веб-сервера.

Рекомендуем