Перейти к содержимому
Владислав Павлович — Системный администратор Linux — Обозреватель Интернета
PHP2Shell (CVE-2026-109618 и CVE-2026-109619): как обновить PHP на сервере и что делать, если фикса нет — Блог

PHP2Shell (CVE-2026-109618 и CVE-2026-109619): как обновить PHP на сервере и что делать, если фикса нет

#php#security#linux

В двадцатых числах октября тикеты с вопросом «Нам пришло письмо про уязвимость в PHP, что делать?» шли один за другим. За неделю я обновил PHP на нескольких десятках серверов: свежие Ubuntu 24.04 и Debian 12, Debian 9 и Ubuntu 20.04, которые давно никто не трогал, серверы под FASTPANEL, ISPmanager, DirectAdmin и HestiaCP. Здесь собрано всё, что пригодилось: что за уязвимость, где брать исправленные сборки, как проверить, что фикс реально встал, и на какие грабли я наступил по дороге.

Важно!
Статус сборок у дистрибутивов и панелей менялся буквально каждый день. Всё, что здесь написано про «есть фикс / нет фикса», — по состоянию на конец октября 2026. Перед работой проверяйте трекер своего дистрибутива и changelog панели (ссылки в конце).

1. Что за уязвимости

Две критические уязвимости в ядре PHP, обе с оценкой 9.2 по CVSS 4.0. В новостях их называют общим именем PHP2Shell (php2shell):

CVE Что это Advisory
CVE-2026-109618 RCE через порчу памяти при out-of-memory GHSA-w8hw-w9fx-j79w
CVE-2026-109619 RCE через устаревшее глобальное состояние между запросами GHSA-6wmf-4rv5-p3rp

Коротко о механике:

  • CVE-2026-109618. Когда PHP упирается в лимит памяти, он аварийно выходит из обработки запроса. Если в этот момент внутренние данные запроса инициализированы не до конца, код очистки потом работает с мусором. Довести PHP до OOM можно специально сформированным multipart/form-data запросом, даже если его размер укладывается в лимиты.
  • CVE-2026-109619. use-after-free из-за состояния, которое переживает запрос. Один из подтверждённых путей — механизм session.upload_progress.
  • Атака работает без авторизации и до запуска кода приложения. Форма загрузки файлов на сайте не нужна: multipart/form-data PHP разбирает сам, ещё до того, как управление дойдёт до вашего кода. Даже «Hello World» на уязвимом PHP — цель.
  • Хуже всего с Apache + mod_php: там очистка запускается даже при неудачном старте запроса, и показанная атака срабатывает раньше кода приложения. Для PHP-FPM полного эксплойта в advisory нет, но и неуязвимым его никто не называет.
  • Затронуты все версии начиная с PHP 5.4.

2. Почему шум именно сейчас

Исследователи сообщили о багах в PHP ещё в августе. 20 октября команда PHP анонсировала экстренный релиз на 21 октября, 14:00 UTC. В тот же день детали утекли раньше срока, и в сети появились прототипы эксплойтов (например, проект PHP-UP-2026). Сами исправления вышли 21 октября чуть раньше объявленного времени:

8.2.35, 8.3.36, 8.4.27, 8.5.12

Версии 8.1 и старше проект PHP не поддерживает — для них официального релиза нет. PHP выложил только минимальные патчи исходников для 7.4, 8.0 и 8.1 для тех, кто собирает сам. Всё остальное — на совести дистрибутивов и вендоров панелей.

Совет
В первых статьях утечка описывала только session.upload_progress. Это неточно: CVE-2026-109618 — про OOM, а upload progress — лишь один из путей для CVE-2026-109619. Отключение session.upload_progress.enabled — полезная мера, но не замена обновлению.

3. Где брать исправленные сборки

Дистрибутивы

Дистрибутив PHP Исправленная версия пакета
Ubuntu 26.04 8.5 8.5.4-0ubuntu1.5
Ubuntu 24.04 8.3 8.3.6-0ubuntu0.24.04.13
Ubuntu 22.04 8.1 8.1.2-1ubuntu2.28
Ubuntu 20.04 7.4 7.4.3-4ubuntu2.29+esm5 — только Ubuntu Pro (ESM)
Debian 13 trixie 8.4 8.4.27-1~deb13u1 (DSA-6574-1)
Debian 12 bookworm 8.2 8.2.35-1~deb12u1 (DLA-4848-1)
Debian 11 bullseye 7.4 7.4.33-1+deb11u14 — только платный Freexian ELTS (ELA-1853-1)
Debian 10 buster 7.3 7.3.31-1~deb10u16 — только ELTS (ELA-1854-1)
Debian 9 stretch 7.0 7.0.33-0+deb9u26 — только ELTS (ELA-1855-1)

Важно!
Ubuntu не меняет номер версии PHP. После обновления php -v на 24.04 всё так же показывает 8.3.6, а на 22.04 — 8.1.2. Canonical переносит исправление в свою версию. Смотреть нужно на версию пакета, а не на php -v. Клиенты регулярно присылают «у меня 8.3.6, а хостер пишет, что нужна 8.3.36» — это нормально, если пакет .13.

Ещё два момента по Ubuntu:

  • Дата сборки в php -v может быть 19 октября — раньше публичного релиза. Canonical собирал пакеты под эмбарго, это не ошибка.
  • Debian 11 бесплатных исправлений не получит: LTS для bullseye закончился 31 августа 2026. Последний пакет в bullseye-security (deb11u12) — без фикса.

Сторонние репозитории

  • packages.sury.org / ppa:ondrej/php выпустили апстримные 8.2.35–8.5.12, а для EOL-веток — пересобранные пакеты с патчем: например, php7.4 1:7.4.33-31+0~20261021… для Debian 11, php8.1 8.1.34-9+0~20261021…. Это бесплатный путь для Debian 11 (подробно в разделе 6).
  • Ubuntu 20.04 и Debian 9 в Sury и PPA давно заморожены — свежих сборок там нет.

Панели

FastPanel. Альтернативные версии лежат в /opt/phpXX (пакеты fastpanel2-phpXX). Обновляются так:

script.sh - Блокнот
/usr/local/fastpanel2/app/updater

Сначала вышли 8.2–8.4, затем 8.5, а в следующие дни — пересобранные 5.4, 7.0, 7.2, 7.4 (у меня на серверах — сборки от 23–26 октября). Список — в changelog FastPanel. На старых ОС (Debian 9 и т. п.) новых сборок нет.

ISPmanager. Пакеты isp-phpXX, номер сборки после версии:

Версии Исправленная сборка
8.2.35, 8.3.36, 8.4.27, 8.5.12 -1781
7.4.33, 8.0.30, 8.1.34 -1782
5.6, 7.0, 7.1, 7.2, 7.3 -1786

Обновление — через «Конфигурация ПО» или apt install isp-php83 (номер версии свой). Только для актуальных ОС: на Debian 9 репозиторий заморожен на сборке -1252, и фикса там нет.

Совет
Не пугайтесь даты в php -v у сборок ISPmanager: там бывает built: Dec 23 2019 или Jun 29 2020. Дата фиктивная, ориентируйтесь на номер сборки пакета.

Plesk. EOL-версии (7.4.33, 8.0.30, 8.1.34) сохранили номер версии, но исправлены сборки от 21 октября 2026.

HestiaCP. Сама PHP не собирает: на Ubuntu ставит из ppa:ondrej/php, на Debian — из Sury. Обновляется обычным apt. На Ubuntu 20.04 PPA заморожен — исправлений нет.

4. Как проверить сервер

Откуда PHP

Первое, что нужно понять, — чей PHP обслуживает сайты. Команда php в консоли — это ещё не тот PHP, который работает у сайтов.

script.sh - Блокнот
# Что за php в CLI и из какого пакета
readlink -f "$(which php)"; dpkg -S "$(readlink -f "$(which php)")"

# Какие сторонние репозитории подключены
grep -rlnE 'ondrej|sury' /etc/apt/sources.list /etc/apt/sources.list.d/

# Версии в /opt (FastPanel, ISPmanager)
for p in /opt/php[0-9]*/bin/php; do echo -n "$p: "; $p -v 2>/dev/null | head -1; done

# Включён ли mod_php в Apache
a2query -m | grep -i php

Частая картина на FastPanel: php -v показывает 7.4, а это просто симлинк на /opt/php74; при этом рядом стоит нативный 8.2 из Debian, на котором в режиме mpm_itk крутятся сайты через mod_php. Обновить нужно и то, и другое.

Стоит ли фикс

script.sh - Блокнот
# Debian / Ubuntu: версия пакета и что предлагает apt
apt update && apt-cache policy php8.2-cli | head -3

# Ubuntu: CVE в changelog
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-cli/changelog.Debian.gz

# RHEL / Alma
rpm -q --changelog php-fpm | head -5

На Debian и в Sury changelog часто пишет только «New upstream version» — тогда сверяйте номер версии с таблицей выше.

Совет
apt changelog в первые часы после выхода пакета отдаёт 404 — changelog на metadata.ftp-master.debian.org выкладывается с задержкой. Смотрите локальный файл после установки: zcat /usr/share/doc/php8.2-cli/changelog.Debian.gz | head.

5. Обновление: дистрибутив, PPA, Sury

Главное правило, которое я выучил на собственной ошибке: список пакетов берите из dpkg, а не шаблоном.

script.sh - Блокнот
P=$(dpkg -l | awk '/^ii  (php[0-9]\.[0-9]-|libapache2-mod-php[0-9])/{print $2}')

# Сначала симуляция: не должно быть строк Remv и unmet
apt install -s --only-upgrade $P | grep -E '^Remv|unmet'

# Потом установка с сохранением текущих конфигов
apt install -y --only-upgrade -o Dpkg::Options::=--force-confold $P

Важно!
apt install 'php8.4*' — плохая идея на серверах с Sury или PPA. apt трактует это как регулярное выражение, под которое попадают и другие php8.x. А метапакеты расширений (php-imagick, php-memcached) тянут дефолтную версию репозитория. У меня так на сервер прилетел PHP 8.5, и update-alternatives тихо переключил на него php в CLI. Cron-скрипты клиента после этого запускались бы уже другой версией.

Если apt update ругается на ключ Sury:

readme.txt - Блокнот
EXPKEYSIG B188E2B695BD4743 DEB.SURY.ORG Automatic Signing Key

— ключ истёк. Обновить его нужно в том файле, который указан в signed-by, а не куда попало:

script.sh - Блокнот
grep -r sury /etc/apt/sources.list.d/   # смотрим signed-by=...
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg

Если signed-by в строке нет, пропишите его явно. Класть ключ в /etc/apt/trusted.gpg.d/ не стоит: оттуда он считается доверенным для всех репозиториев.

6. Debian 11: PHP 7.4 из Sury аккуратно

Самый частый случай: Debian 11, сайты на 7.4 в mpm_itk, а Debian фикса не даст. Ставим патченную сборку Sury, но только PHP — системные библиотеки и метапакеты остаются от Debian.

Бэкап:

script.sh - Блокнот
dpkg -l | awk '/^ii  (php|libapache2-mod-php|libpcre2-)/{print $2"="$3}' > /root/php-before-sury.txt
tar czf /root/etc-php-before-sury.tgz /etc/php /etc/apache2/mods-available/php7.4.*
php -m | sort > /root/php-modules-before.txt

Репозиторий и пиннинг:

script.sh - Блокнот
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ bullseye main" \
  > /etc/apt/sources.list.d/php-sury.list

cat > /etc/apt/preferences.d/sury-php74 <<'EOF'
Package: *
Pin: origin packages.sury.org
Pin-Priority: 50

Package: php7.4* libapache2-mod-php7.4 libpcre2-*
Pin: origin packages.sury.org
Pin-Priority: 990
EOF

apt update && apt-cache policy php7.4-cli libpcre2-8-0 php-common

Кандидат для php7.4-cli должен быть 1:7.4.33-31+0~20261021…, а php-common — остаться дебиановским.

Почему так:

  • libpcre2-* в пиннинге обязателен. Сборке Sury нужна libpcre2-8-0 >= 10.38, а в Debian 11 есть только 10.36. Без неё apt выдаст unmet dependencies.
  • php-common в пиннинг не добавляем. Версия 2:101 из Sury конфликтует с дебиановскими php-imagick, php-memcached, php-igbinary, php-msgpack — apt захочет их удалить. Сайтам, которые их используют, это бы не понравилось. Сборка 7.4 из Sury прекрасно работает с дебиановским php-common 2:76.
  • Префикс 1: в версии Sury — это epoch, из-за него версия Sury считается выше дебиановской. Так и должно быть.

Симуляция и установка:

script.sh - Блокнот
P="$(dpkg -l | awk '/^ii  (php7\.4|libapache2-mod-php7\.4|libpcre2-)/{print $2}')"
apt install -s --only-upgrade $P | grep -E '^(Inst|Remv)|unmet'   # только Inst
apt install -y --only-upgrade -o Dpkg::Options::=--force-confold $P

diff /root/php-modules-before.txt <(php -m | sort) && echo "модули без изменений"

ionCube Loader для 7.4 после замены продолжает работать — ABI тот же. Но проверить php -v всё равно стоит.

Откат:

script.sh - Блокнот
rm /etc/apt/sources.list.d/php-sury.list /etc/apt/preferences.d/sury-php74 && apt update
apt install --allow-downgrades $(cat /root/php-before-sury.txt)
systemctl restart apache2

7. Перезапуск — без него обновление не считается

Запущенный процесс держит старый PHP в памяти, пока его не перезапустят.

Как работает PHP Что делать
PHP-FPM systemctl reload-or-restart phpX.Y-fpm
Apache + mod_php (mpm_itk в FastPanel) systemctl restart apache2 — reload и graceful не подходят
FastCGI / mod_fcgid (fcgi в FastPanel) restart apache2 + pkill -f php-cgi
ISPmanager плюс инстансы apache2-isp* / httpd-isp@phpXX
script.sh - Блокнот
systemctl list-units --type=service --state=running --no-legend --plain \
  | awk '{print $1}' | grep -E '^(apache2|httpd|php.*fpm)' | xargs -r -n1 systemctl restart
pkill -f php-cgi

Проверка, что ничего не осталось на старом PHP:

script.sh - Блокнот
grep -slE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps

Пустой вывод — всё работает на новых бинарях. Если в списке системные процессы со старой libpcre2 (systemd, sshd, cron) — к PHP это не относится, их можно перезапустить отдельно.

8. Если фикса нет — митигация

Для Debian 9/10, Ubuntu 20.04, EOL-версий без сборок от панели остаются две меры.

Отключить session.upload_progress

Закрывает один путь атаки для CVE-2026-109619. Кладём drop-in в scan-dir каждой версии и каждого SAPI. В Debian у cli, fpm, cgi и apache2 — свои conf.d, и правка одного не трогает остальные.

Нативный PHP:

script.sh - Блокнот
for d in /etc/php/*/*/conf.d; do
  echo 'session.upload_progress.enabled = Off' > "$d/90-cve-2026-109618.ini"
done

Версии в /opt (FastPanel, ISPmanager) — с автоопределением scan-dir:

script.sh - Блокнот
for p in /opt/php[0-9]*/bin/php; do
  d=$($p --ini 2>/dev/null | awk -F': *' '/Scan for additional/{print $2}' | tr -d '"')
  f=$($p --ini 2>/dev/null | awk -F': *' '/Loaded Configuration/{print $2}' | tr -d '"')
  if [ -d "$d" ]; then
    echo 'session.upload_progress.enabled = Off' > "$d/90-cve-2026-109618.ini"
  elif [ -f "$f" ]; then
    cp -a "$f" "$f.bak-cve"; echo 'session.upload_progress.enabled = Off' >> "$f"
  else
    echo "SKIP $p"
  fi
  echo -n "$p: "; $p -r 'var_dump((bool)ini_get("session.upload_progress.enabled"));' 2>/dev/null
done

| tr -d '"' нужен не просто так: PHP 8.5 печатает пути в --ini в кавычках, и без него проверка -d молча не проходит.

Важно!
Проверять нужно через веб, а не только CLI. У CLI и FPM разные конфиги. Положите в корень сайта временный файл с ini_get("session.upload_progress.enabled") и PHP_SAPI, откройте его и сразу удалите. Один клиент выставил параметр через панель только для PHP 8.4, а большинство сайтов работали на 7.4 — и были уверены, что закрыты.

Блокировка multipart в Nginx

Закрывает главный вектор CVE-2026-109618. В http-контекст:

nginx.conf - Блокнот
map $content_type $cve_mp {
    default 0;
    "~*multipart/form-data" 1;
}

В server сайта (в FastPanel — файл ручных настроек .includes):

nginx.conf - Блокнот
if ($cve_mp) { return 403; }

Цена — ломаются все загрузки файлов на сайте, включая медиатеку WordPress и формы с вложениями. Ставить только по согласованию с владельцем.

Сайт, закрытый Basic Auth в .htaccess, без пароля не атаковать: Apache возвращает 401 до запуска PHP. Проверить можно маркером:

script.sh - Блокнот
echo '<?php touch("/tmp/cve_hit");' > /path/to/site/_t.php
curl -sk -o /dev/null -w '%{http_code}\n' -F x=1 https://site/_t.php   # ждём 401
ls /tmp/cve_hit   # файла быть не должно
rm -f /path/to/site/_t.php

9. Грабли из тикетов

Сайты «пропали» вместе с конфигами. На одном сервере в fastpanel2-sites не было ни одного .conf — только .includes. Оказалось, сайты выключили в панели: FastPanel при этом удаляет конфиги Nginx и Apache, и они пересоздаются при включении. .includes с ручными настройками при этом сохраняется — туда и можно заранее положить правило.

ucf заменил php.ini. При переезде 8.2 с Debian на Sury в логе было Replacing config file /etc/php/8.2/cgi/php.ini. Это ucf, а не dpkg, и --force-confold на него не действует. Он заменяет файл молча только если его не меняли руками, но проверить memory_limit и upload_max_filesize после такого стоит.

Загрузка картинок в WordPress сломалась после «снятия всех ограничений». Нашлось file_uploads=0 для PHP 8.4, которое выставили через панель в начале тикета. async-upload.php при этом отвечает 200 — запрос доходит до PHP, а WordPress возвращает JSON с ошибкой. По логам кажется, что всё хорошо. Смотрите ответ в DevTools.

Зависимости сломаны до вас. apt install отказывался что-либо ставить из-за unmet dependencies у python3-certbot — кто-то когда-то удалил python3-cryptography. apt --fix-broken install -s показал, что нужно лишь доставить два пакета. Всегда сначала симуляция: если --fix-broken хочет что-то удалить — разбираться руками.

После патча митигацию можно снимать. Но только с исправленных версий. На одном сервере FastPanel пересобрал 7.0, 7.2 и 7.4, а 7.1 остался со сборкой 2022 года — с него session.upload_progress = Off снимать не стал.

10. Порядок действий

  1. Понять, какие PHP реально обслуживают сайты: нативный, /opt, mod_php или FPM.
  2. Есть исправленный пакет → обновить (список из dpkg, сначала симуляция), перезапустить Apache/FPM, проверить /proc/*/maps.
  3. Панель → updater / обновление isp-php*, проверить даты и номера сборок.
  4. Debian 11 → Sury с пиннингом (без php-common, с libpcre2).
  5. Фикса нет (Debian 9/10, Ubuntu 20.04, EOL без сборок) → session.upload_progress = Off, по согласованию блокировка multipart, и честно сказать клиенту про переезд.

Отключение параметра и блокировка в Nginx снижают риск, но не убирают баг из PHP. Это временная мера, а не решение. На серверах с Debian 9 и PHP 7.0 закрыть уязвимость можно только переездом.

Список литературы

php2shell-cve-2026-109618-i-cve-2026-109619-kak-obnovit-php-na-servere-i-chto-delat-esli-fiksa-net.md
14 мин
Просмотров: 1

Комментарии (0)

Комментариев пока нет. Будьте первым!

Оставить комментарий
Готово
Интернет