Запрос попадает в PHP раньше вашего контроллера
Приложение может отклонить загрузку, когда PHP уже обработал часть её тела. Именно этот порядок требует исследовать уязвимость среды выполнения иначе, чем небезопасный контроллер загрузки. Для CVE-2026-109618 и CVE-2026-109619, обсуждаемых под названием PHP2Shell, главный вопрос состоит в том, где данные запроса пересекают границу безопасности внутри интерпретатора.
Я рассматриваю три уровня: подтверждённый выпуск исправлений, поведение среды выполнения в исходном коде и механизм, предложенный в рассмотренных реализациях. Оценка датирована 21 октября 2026 года и основана на статическом анализе PHP 8.4.4, соответствующего кода PHP 8.5.4, реализаций доказательства концепции, публикаций и рассмотренной демонстрации. Независимое воспроизведение эксплуатации не проводилось.
Что подтвердила команда PHP
В объявлении PHP от 20 октября подтверждены две критические уязвимости с оценкой CVSS v4.0 9.2 каждая. Выпуск исправлений назначен на 21 октября, 14:00 UTC. В изученном предварительном уведомлении затронутые и исправленные версии, а также технические подробности отложены до выпуска. Само уведомление не называет номера CVE и не связывает первопричину с отслеживанием загрузок. Эти идентификаторы и описание RCE приводятся в связанных публикациях. [1, 2]
В отчёте Huntress воспроизведение CVE-2026-109618 приписывается watchTowr. При этом Huntress сообщает, что по состоянию на 20 октября самостоятельно не воспроизводила уязвимости и не наблюдала связанных атак в средах партнёров. Рассмотренная демонстрация показывает эффекты, соответствующие выполнению команд и созданию файлов, в том числе на WordPress и Drupal. Соответствие показанной реализации демонстрационного инструмента рассмотренным реализациям не установлено. Эти наблюдения требуют срочной реакции, но точную причину и условия необходимо установить. [2]
Почему важно отслеживание загрузки
PHP может записывать ход загрузки в сессию во время разбора multipart-запроса. Состояние включает общий счётчик байтов, счётчики отдельных файлов, метаданные и признаки завершения. Механизм работает при обработке запроса, поэтому видимая форма загрузки не описывает всю поверхность атаки среды выполнения. Функция и частота её обновления описаны в документации PHP. [3]
В изученном коде FILE_START создаёт состояние файла, FILE_DATA обновляет счётчики, а END завершает отслеживание или очищает данные. Указатель общего счётчика сохраняется при первоначальном создании; указатель текущего файла обновляется для каждого файла. Запись счётчиков предшествует вызову функции обновления сессии. Поэтому нельзя автоматически считать, что аутентификация в коде приложения защищает более ранний разбор запроса. Проверка доступа на прокси до передачи запроса PHP находится на другой границе. [4]
Предполагаемая цепочка по этапам
Одна из рассмотренных реализаций доказательства концепции связывает первый идентификатор с общим счётчиком байтов, а другая связывает второй со счётчиком текущего файла. Оба описывают устаревший указатель при повторных обновлениях прогресса. Это привязки из рассмотренных реализаций, а не из изученного уведомления разработчика. Их модель требует достижимости обработчика, создания состояния, освобождения памяти до последующего обращения и полезного контроля над повреждённым состоянием.
Для RCE дополнительно нужны достижимый путь выполнения, адреса и структуры, совместимые с конкретной сборкой. Демонстрация показывает этап раскрытия адресов; извлечение символов локальным фреймворком не доказывает аналогичную удалённую утечку. Ранние выделения памяти не доказывают последующее занятие конкретного освобождённого участка, а две отправки с задержкой не доказывают два соответствующих серверных callback. Это отдельные условия. Рабочая цепочка должна их связать; в доступных локальных файлах эта связь остаётся неполной.
Время жизни: кто ещё владеет массивом?
Use-after-free означает обращение к памяти после завершения времени жизни выделенного объекта. Обозначим живой массив прогресса как P, а отдельно декодированный массив сессии как D. Одинаковое содержимое не делает их одним объектом. На обычном успешном пути PHP 8.4.4 функция увеличивает счётчик ссылок перед помещением P в дерево сессии. Сброс дерева удаляет его ссылку, но состояние прогресса продолжает владеть P. Замена D не освобождает P. Соответствующее увеличение счётчика есть и в изученной функции PHP 8.5.4. [4, 5, 6, 7]
Эта модель владения остаётся неразрешённым звеном предложенного объяснения. Она не исключает ни одну CVE и не опровергает отдельную демонстрацию. Полный разбор должен указать другой путь или условие, нарушающее гарантию времени жизни. Даже разделение в конце запроса требует анализа порядка: при отключённой очистке старое дерево сессии ещё владеет исходным массивом в момент последней записи счётчика, до принудительного обновления и завершения. Самого устаревшего указателя недостаточно без последующего обращения. [4]
Отравление сессии пересекает другую границу
Рассмотренный сторонний лабораторный отчёт описывает другой путь к выполнению кода: управляемое атакующим содержимое сессии попадает в механизм включения файлов приложения. В намеренно уязвимой тестовой точке входа параметр запроса напрямую передаётся в include(). Профиль persistence отключает очистку прогресса и меняет интервалы обновления и ограничения multipart. Протокол в отчёте сообщает о выполнении кода в этих условиях. Это показывает, какую границу приложения следует проверять, но само по себе не устанавливает механизм повреждения памяти ни одной из CVE.
В том же отчёте описаны выбор идентификатора сессии без заголовка Cookie, фиксация сессии в намеренно упрощённом сценарии входа и сбой сериализации при наличии символа вертикальной черты в ключе прогресса. Эти наблюдения зависят от обработчика, сериализатора, политики сессий и поведения приложения. Содержимое сессии должно оставаться данными; пути для include/require следует выбирать из контролируемого списка разрешённых значений, а при переходе к аутентифицированному состоянию менять идентификатор сессии. Наличие записи прогресса само по себе не доказывает повторное использование освобождённой памяти.
Результат uid=0 в протоколе описывает пользователя лабораторного процесса. Скрипт запускает встроенный сервер PHP без указания пользователя с ограниченными правами; такой вывод не доказывает повышение привилегий или получение root-доступа к хосту. Для защиты это существенное различие: устранение небезопасного включения файлов и исправление политики сессий закрывают риски приложения, а ошибка безопасности памяти среды выполнения по-прежнему требует отдельного исправления поставщика.
Затронутые версии и совместимость эксплойта
Изученное уведомление разработчика не содержит подтверждённого диапазона затронутых версий или матрицы исправлений. Широкие утверждения о PHP начиная с версии 5 остаются утверждениями публикаций. PHP 8.4.4 служит базой нашего анализа исходников; PHP 8.5.4 фигурирует в предоставленном материале и профиле демонстрации. Ни одно наблюдение не устанавливает весь диапазон. Это срез доступных доказательств; решения по пакетам должны опираться на подробный бюллетень разработчика. [1, 8]
Уязвимость может затрагивать несколько веток, тогда как конкретный эксплойт поддерживает одну архитектуру, сборку или серверную интеграцию. Неудача PoC также не доказывает безопасность системы. Демонстрация содержит профили Apache prefork с mod_php и PHP-FPM, но не устанавливает охват всех SAPI. Учитывайте реально обслуживающую запросы сборку и интеграцию, а не только версию CMS или бинарного файла CLI.
Рассмотренный сторонний отчёт об экспериментах сообщает об отсутствии выполнения команд при попытках эксплуатации повреждения памяти на php:8.4.4-cli, php:8.4.4-fpm и php:8.2.34-fpm, несмотря на локально определённые адреса. Это результаты, заявленные авторами отчёта, а не испытания для этой статьи. Они не устанавливают безопасные версии и не позволяют переносить успешный пример с включением файлов на стандартные установки PHP. Автор также предлагает оценка CVSS v3.1; это отдельная авторская оценка, а не преобразование рейтинга v4.0, опубликованного PHP.
Какие настройки меняют условия?
В предполагаемом пути session.upload_progress.enabled включает отслеживание; name задаёт поле активации; freq и min_freq управляют порогами обновления; cleanup управляет удалением данных после обработки POST. Обработчики сессий и сериализация влияют на сохранение состояния. Это параметры для проверки, а не подтверждённый перечень предпосылок обеих CVE. В частности, очистка в конце не гарантирует предотвращения более ранней ошибки памяти. [3, 9]
SAPI, буферизация сервера, ограничения тела запроса и тайм-ауты влияют на поступление данных в парсер. Клиентская пауза после буферизации может не стать паузой между callback PHP. Проверяйте эффективную конфигурацию веб-процесса и каждого пула FPM: CLI может использовать другую. Если отслеживание загрузок не нужно, рассмотрите его отключение как превентивное усиление защиты. Изученные данные не подтверждают, что этого достаточно для обходного устранения обеих CVE. [3, 9]
Рассмотренная конфигурация лаборатории выявляет ещё одну важную деталь конфигурации: session.upload_progress.freq=1 задаёт абсолютный интервал в байтах, а 1% зависит от размера запроса. В профиле тестирования повреждения памяти используется первый вариант, хотя комментарии описывают стандартные настройки. В профиле проверки сохранения данных установлены cleanup=0 и min_freq=0. Прежде чем переносить результаты лаборатории на рабочую среду, сравните действующие значения вместе с единицами измерения. [4, 9]
Масштаб последствий задают права процесса
Выполнение команд от www-data уже может привести к компрометации приложения. В зависимости от прав процесс способен читать учётные данные БД, менять доступные для записи файлы и обращаться к внутренним сервисам. Общие служебные аккаунты и каталоги могут расширить последствия на другие приложения. Это риски для проверки в конкретной среде, а не утверждение о показанных последствиях. Важно понять, что процесс может читать, изменять и к чему обращаться.
Huntress рекомендует отслеживать неожиданные дочерние процессы PHP и веб-сервера. Сопоставляйте дерево процессов с запросами, временем, новыми файлами и исходящими соединениями: легитимные приложения тоже запускают инструменты, а выполнение возможно без новой оболочки. Проверки старых файлов в локальном триггере и неполный разбор FastCGI в фреймворке дополнительно показывают, почему результат инструмента требует привязки к событию. Описанный Huntress более ранний PHP-инцидент прямо назван не связанным с PHP2Shell; его нельзя выдавать за эксплуатацию этих CVE. [2]
Проверяйте исправление в обслуживающем процессе
Сопоставьте публичные адреса с пакетами или образами PHP, SAPI, расширениями и эффективными настройками. Учтите стороннее ПО и хостинг со встроенным PHP. Сверьте подробный бюллетень с пакетами дистрибутива или поставщика, включая перенесённые исправления. Замените или перезапустите затронутые процессы и убедитесь, что запросы обрабатывает исправленная сборка. Для систем без поддерживаемого обновления безопасности подготовьте обновление среды или сокращение внешней доступности.
Техническая цель состоит в связи уязвимой операции, условий её достижимости и кода, восстанавливающего необходимый инвариант. Чистый результат рассмотренных реализаций не заменяет такую связь. При проверке исправлений я прослеживаю утверждение до развёрнутого процесса и проверяю, что нужное исправление действительно применяется. Если вашей PHP-инфраструктуре нужна такая оценка, работа начинается с действующей среды и проверяемых данных.
Источники
- [1] PHP: предварительное уведомление об исправлениях
- [2] Huntress: отчёт PHP2Shell (предоставленный текст)
- [3] Руководство PHP: прогресс загрузки в сессии
- [4] PHP 8.4.4: callback сессии и владение памятью
- [5] PHP 8.4.4: операции хеш-таблиц
- [6] PHP 8.4.4: уничтожение по счётчику ссылок
- [7] PHP 8.5.4: реализация сессий
- [8] watchTowr: вопросы о PHP RCE
- [9] Руководство PHP: настройки сессий
Нужна такая проверка для вашего продукта?
Помощь с исправлениями и проверкаХотите сами научиться находить такие уязвимости?
Курс аудита безопасности кода, индивидуально