
24 октября 2026 honeypot связал 35 POST уязвимости PHP2Shell с 35 процессами /bin/sh. Их напрямую породили воркеры Apache на PHP 8.4.26. Признак успеха — родительский процесс оболочки и падение воркера, а не строка access log.
Коротко
- 35 полных POST стадии выполнения совпали с 35 процессами /bin/sh. Родитель — воркер Apache, uid 33, фиксация событиями Tetragon.
- Два адреса, 15.204.59.70 и 162.35.99.94, — два эпизода с одним шаблоном, не доказанные два оператора. Ещё два адреса дали только попытки.
- Цели: PHP 8.4.26, mod_php, memory_limit 128 МиБ. Ошибки «Unknown on line 0» до кода приложения. Записи согласуются с CVE-2026-109618 и не подтверждают CVE-2026-109619.
- Успех читается по родителю оболочки и падению воркера. Утечка — cookie PHPSESSID на 819 200 байт. Пока бюллетень PHP не подтвердил исправленную сборку, обновляют интерпретатор и выключают session.upload_progress.enabled.
Что honeypot подтвердил 24 октября
Honeypot 24 октября 2026 года подтвердил эксплуатацию уязвимости PHP2Shell по процессам, а не по «похожему» HTTP. 35 полных POST стадии выполнения совпали один к одному с 35 стартами /bin/sh. Родителем каждой оболочки был воркер Apache с uid 33. Родительский процесс зафиксирован событиями Tetragon, а не выведен из кода ответа.
Цели — три одноразовых PHP-honeypot, T1, T2 и T3. Шесть сенсоров закрыли окно свидетельств между 19:26 и 19:31 UTC. Сами оболочки стартовали раньше, днём. Цифры относятся к этому снимку.
Корреляцию собрали из трёх слоёв: двусторонний захват пакетов, записи Apache о запросе и PID воркера, события процессов Tetragon. Склейка шла по порту источника, времени, идентификатору воркера и порядку запросов. Код состояния HTTP за доказательство выполнения не принимали.
Снимок: 3 459 запечатанных сегментов, 10 948 файлов манифеста, 11,21 ГБ. Перечисленные размеры и SHA-256 совпали с локальными копиями. В записях Apache и Tetragon в формате JSONL битых строк не нашли.
«Полный» POST стадии выполнения — запрос, пересобранный на стороне цели и дошедший до финала цепочки, плюс отдельный старт оболочки. Код 200 в эту проверку не входит. 35 подтверждённых запусков нельзя складывать с адресами, которые слали трафик той же формы без оболочки.
Подтверждённые запросы шли открытым текстом, HTTP/1.1 на порт 80. Отчёт не утверждает, что цепочка невозможна после TLS: на этих целях шифрованный порт не попал в подтверждённые запуски.
Два адреса — это два оператора?
Нет. Отчёт фиксирует два эпизода с адресов-источников и не называет их двумя операторами. Общий инструмент не доказывает общее управление. Разные IP и разные команды проверки не доказывают разных авторов. Обоснованный вывод: два эпизода адресов с близким инструментарием.
Два эпизода с адресов-источников и близкий инструментарий, без вывода об общем операторе.
Эпизод A дал 3 оболочки, все на T1. Эпизод B дал 32 оболочки с 16:46 до 18:57 UTC: 19 на T2, 10 на T3 и 3 на T1. Вместе 35. B прогонял шаблон по всем трём стендам, A остался на одной цели.
| Эпизод | Источник | Цели | Cookie утечки | Оболочки | Команда |
|---|---|---|---|---|---|
| A | 15.204.59.70 | T1 | 3 | 3 на T1 | Один и тот же id, файл-доказательство и wget |
| B | 162.35.99.94 | T1, T2, T3 | 49 | 3 на T1, 19 на T2, 10 на T3 | PHP CLI, curl, wget, затем проверки имени, хоста и архитектуры |
Оба эпизода шли одним четырёхстадийным шаблоном и в одном порядке: давление на память, утечка, снова давление, запрос выполнения. Первые три раскодированных тела запроса побайтово совпали у обоих источников и на всех трёх целях. Это признак одного семейства клиента, а не одного центра управления.
Тело финальной стадии менялось вместе с диапазоном адресов, слитым с конкретной цели, и вместе с командой оболочки.
Оба подтверждённых адреса на 24 октября числились за коммерческим хостингом. 15.204.59.70 входил в сеть OVH US LLC, префикс 15.204.0.0/16. PTR vps-b225fb62.vps.ovh.us разрешался обратно в тот же адрес. 162.35.99.94 входил в сеть Interserver, Inc, префикс 162.35.96.0/19. PTR vps3749638.trouble-free.net подтверждающей A-записи не имел.
Отдельно 104.28.208.84 и 144.48.4.242 слали запросы той же формы. Рядом были ошибки PHP или падения воркера. Оболочки с родителем Apache не было, cookie размера утечки тоже не было. Отчёт считает их индикаторами попытки.
Уязвимость PHP2Shell — баг движка или дыра в CMS?
Наблюдаемая цепочка указывает на движок PHP, а не на баг конкретной CMS. Ошибки вспыхивали на старте запроса, до кода приложения. Цели работали на PHP 8.4.26 под Apache с mod_php. Лимит памяти PHP — 128 МиБ, то есть 134 217 728 байт.
В отчёте схема названа single-worker Apache mod_php. Это не сводка 35 оболочек к одному PID. Первая оболочка на T1 вышла из PID 31 и сразу завершилась по сигналу 11. Следующие тоже имеют родителя-воркера, отчёт не склеивает их в один процесс.
В ошибке «Allowed memory size of 134217728 bytes exhausted» фигурируют попытки выделить 4 096 байт на первом давлении и 20 480 байт на втором. Пометка — «Unknown on line 0». Сама по себе она не доказывает дыру ядра: так PHP пишет, когда у сообщения нет строки исходника. Вес выводу придаёт момент: старт запроса, до пользовательского кода, одна и та же цепочка на трёх honeypot. Номер исправленного релиза этот отчёт не называет: патч берут из бюллетеня разработчика PHP.
Ошибки пришлись на старт запроса, до кода приложения, и согласуются с уязвимостью движка, а не конкретной CMS.
Для сайта на WordPress или 1С-Битрикс практический вывод делает не отчёт, а версия интерпретатора. Если CMS крутится на PHP 8.4.26 с mod_php, закрывать нужно сборку PHP и способ запуска, не только плагины. Проверку WordPress и 1С-Битрикс имеет смысл ставить после сверки интерпретатора.
Записи согласуются с CVE-2026-109618 и с семейством публичных доказательств, которые работают только по HTTP. Исследователи Wiz описывали PHP2Shell вокруг прогресса загрузки и утечки памяти. Отдельный путь CVE-2026-109619 эти записи не устанавливают.
Наблюдение поддерживает уже описанный обход ASLR по утечке, а не новый примитив порчи памяти. Слово, похожее на адрес, в финальном теле держало один и тот же сдвиг от доминирующего указателя на T1, T2 и T3.
Утечка возвращалась раздутым значением cookie сессии. Таких записей в журнале Apache 52: у эпизода A — три на T1, у эпизода B — четыре на T1, 29 на T2 и 16 на T3. Каждое значение раскодируется в 819 200 байт. В одной из утечек одно доминирующее слово повторилось 1 095 раз.
Во всех четырёх телах были поля PHPSESSID и PHP_SESSION_UPLOAD_PROGRESS. Отчёт относит цепочку к семейству утечки через прогресс загрузки. Отдельная функция загрузки в CMS в этих записях не фигурирует.
Трафик близок к публичному семейству HTTP-only PoC. Клиент 9bie считает относительный адрес тем же типом сдвига. Пакеты не называют конкретную программу и ревизию. Ревизия exploit.py, просмотренная 24 октября, отбрасывала указатели ниже 0x7f0000000000. У ранних успешных цепочек слов выше порога в cookie не было, две поздние cookie на T3 такие указатели содержали. Уверенность средняя: захват узнаёт семейство запроса и не называет реализацию клиента.
Размер HTTP-тела нельзя приравнивать к расходу памяти PHP. Раскодированное тело стадии утечки — 33 128 677 байт, около 31,6 МиБ, при лимите 128 МиБ.
Почему код ответа HTTP ничего не доказывает?
Финальный запрос убивает воркер Apache уже после старта оболочки, и обычной строки access log у этой стадии нет. HTTP 200 на предыдущих стадиях отчёт критерием успеха не считает. Segmentation fault родителя не откатывает то, что дочерний процесс уже сделал.
Запросы шли POST на «/» и «/index.php», multipart, без заголовка User-Agent. Граница multipart — десять символов. У стадий с Transfer-Encoding: chunked порядок заголовков: Host, Content-Type, Transfer-Encoding, Connection. У стадий фиксированной длины: Host, Content-Type, Content-Length, Connection. Для детекта это почерк клиента, не подпись CVE.
Первое давление несло пять нумерованных параметров вида q…=v. POST без User-Agent и с сотнями однотипных полей — повод для проверки, а не признак компрометации.
Размеры тел сняты с полной пересборки TCP на стороне цели. Обрамление chunk в размер не входит. Одни и те же три первых раскодированных тела нашлись у обоих источников и всех трёх целей.
- A1, давление: 1 060 211 байт, 973 части, из них 859 полей files*, передача chunked. В журнале запрос завершился с HTTP 200, память уже была исчерпана.
- B1, утечка: 33 128 677 байт, 918 частей, из них 900 полей x*, chunked. В полном ответе — PHPSESSID на 819 200 байт и предупреждения старта сессии.
- A2, давление: 279 369 байт, 997 частей, из них 880 полей files*, фиксированная длина. Снова исчерпание лимита, попытка выделить 20 480 байт.
- B2, выполнение: 451 384 байт, 998 частей, среди них 500 полей s* и 495 полей t*, фиксированная длина. Плюс значения, похожие на адреса, и строка команды.
Ошибки PHP на A1 и A2 возникали до скрипта. B1 могла отдать большой cookie или уронить воркер. B2 могла уронить воркер уже после старта оболочки и не оставить завершённой строки access log. Тот же дайджест тела B1 встречался в захватах, где воркер только падал, без подтверждённого RCE. Совпадение тела узнаёт семейство запроса.
Доставку утечки сверили с захватом. 51 из 52 ответов собран целиком, с подтверждением TCP до конца ответа: T1 — 7 из 7, T2 — 28 из 29, T3 — 16 из 16. Один файл на T2 начинается с середины ответа. Cookie в журнале у этого файла есть, полная доставка этим файлом не доказана.
Apache с mod_php исполняет PHP внутри рабочего процесса httpd. Прямой родитель httpd у /bin/sh — сигнал, что выполнение вышло из веб-воркера. На PHP-FPM подозрительным родителем был бы процесс пула, не httpd. uid 33 на Debian и Ubuntu чаще всего www-data. Права оболочки равны правам воркера. Повышения привилегий не зафиксировано.
На проверках сайтов цепочка, которая роняет воркер, в отчёте сканера числится обрывом или 502 балансировщика. На хосте к этому моменту уже может быть дочерний sh, curl или php с родителем воркера. Пентест веб-приложений здесь смотрит интерпретатор и SAPI, а не только маршруты CMS.
Как шли два эпизода по времени
Эпизод A: проба путей в 13:47 UTC, давление на память около 15:16, первая оболочка 15:35:38.673, ещё две в 16:04. У всех трёх совпали хеш тела финальной стадии и команда. Цель одна.
В 15:35:32.793 с порта 54952 началось A1, тело 1 060 211 байт. Около 15:35:33 PHP исчерпал 134 217 728 байт, пытаясь выделить 4 096, пометка «Unknown on line 0». Access log завершился с HTTP 200.
В 15:35:34.127 с порта 38108 пошла B1, тело 33 128 677 байт. В 15:35:34.205 Apache записал запрос на воркере PID 31 и cookie размера утечки. В 15:35:37.429 с порта 38122 началось A2, тело 279 369 байт, попытка выделить 20 480 байт.
В 15:35:38.238 с порта 38126 начался финальный POST, тело 451 384 байт. В 15:35:38.673 Tetragon зафиксировал apache2, затем /bin/sh с uid 33, затем /usr/bin/id. В 15:35:39.379 PID 31 вышел по сигналу 11. Между стартом оболочки и смертью воркера — 706 мс. Строки access log на финальный запрос нет. A1, B1 и A2 записаны на том же PID 31.
Команда писала вывод id во временный файл и в корень сайта, затем звала wget. Запись в корень упёрлась в файловую систему только для чтения, wget в системе не было. Оболочка к этому моменту уже работала. На боевом хосте с записываемым корнем и wget в PATH та же команда оставила бы файл и исходящую загрузку.
Эпизод B появился в 16:36 уже с ответами размера утечки. Около 16:44–16:46 шли повторные попытки. Первая подтверждённая оболочка — в 16:46. До 18:57 Tetragon насчитал 32 старта: 19 на T2, десять на T3, три на T1. Команды менялись между PHP CLI, curl и wget; в поздних URL сидели id -un, имя хоста и uname -m. Cookie размера утечки у B — 49, из них 48 собраны целиком. Три cookie эпизода A тоже собраны целиком. Окно сенсоров 19:26–19:31 UTC — сбор свидетельств, не момент взлома.
До рестарта сервиса стоит снять список процессов с PPID, дескрипторы и сокеты, журналы Apache и PHP, захват пакетов. Рестарт рвёт связь воркера и живой оболочки.
Что делать, если на сервере PHP 8.4.26
Если на периметре ещё PHP 8.4.26 под mod_php, стенд в той же поверхности, что honeypot. Сверять пакет нужно с бюллетенем разработчика PHP. Отчёт не публикует матрицу всех веток 8.x и не называет номер исправленной сборки. Образы, куда версия зашита при сборке, после обновления пакета нужно пересобрать. Пока канал обновлений не подтвердил исправленную сборку, выключите session.upload_progress.enabled: через эти поля в цепочке фигурируют PHPSESSID и PHP_SESSION_UPLOAD_PROGRESS. После патча настройку возвращают, только если прогресс загрузки нужен приложению.
Имеет смысл уйти с mod_php на PHP-FPM с отдельным пользователем пула. Права оболочки равны правам воркера. Отдельный пользователь не закрывает дыру движка, но режет то, что оболочка прочитает и запишет. Проверьте исходящие от www-data, apache или пользователя пула: в эпизоде A команда звала wget, в эпизоде B мелькали curl и wget. Рядом смотрите новые файлы во временном каталоге и в корне сайта. Сканирование уязвимостей версию интерпретатора не заменяет.
Имеет смысл искать связку, а не один признак:
- исчерпание memory_limit с «Unknown on line 0» на старте запроса, до кода приложения;
- аномально большой PHPSESSID: в этом захвате значение раскодировалось в 819 200 байт;
- смерть воркера по segmentation fault без обычной строки access log на последнем POST;
- дочерние sh, dash, bash, php, curl или wget с родителем httpd, apache2 или php-fpm;
- исходящие подключения этих процессов и записи во временный каталог или в корень сайта.
Огромный cookie, multipart без User-Agent и сотни однотипных полей — повод для проверки, а не признак компрометации. Подтверждение — оболочка от воркера, файл, исходящее соединение, падение. Два адреса дали ошибки и падения воркера без оболочки и без cookie размера утечки. Считать их RCE — завышать инцидент.
Индикаторы тел запроса
Ниже SHA-256 раскодированных тел multipart, не всего TCP-потока. A1 и B1 шли с Transfer-Encoding: chunked, A2 и B2 — с Content-Length. Дайджест B2 в таблицу не входит: он менялся вместе с адресами цели и командой. Совпадение A1–A2 узнаёт семейство. Исход выполнения подтверждает только родитель оболочки.
| Тело | Поля | SHA-256 раскодированного тела |
|---|---|---|
| A1 | files* доминируют, 859 из 973 частей | 8e8fc166e6b326d20bb7611d8ef3cee3bd5da46447985312c1492ab0a44d1eb6 |
| B1 | x* доминируют, 900 из 918 частей | b6c547e0e9a2bb26607df1521de96f4d5c9adf4621c28b7ab491587b21ae40f2 |
| A2 | files* доминируют, 880 из 997 частей | 7af66c8f35c5fa6b47a539da6dca361d19543d9f62d61e4af891c2802bd5e236 |
Тот же дайджест B1 есть в захватах попыток без RCE. Поля s* и t* (500 и 495 частей в разобранном B2) плюс строка команды отличают стадию выполнения, но хеш этой стадии стабильным индикатором не будет.
Одна строка memory exhausted без большого cookie, без падения и без дочернего процесса — ещё не вердикт. На эпизоде A разведка путей началась в 13:47, за два часа до оболочки. Публичный эксплойт на боевом контуре гонять не нужно: цепочка выжигает память и роняет воркеры. Реагирование на инцидент здесь начинается с родителя процесса и журнала ошибок, а не с кода ответа.
Частые вопросы
Что такое PHP2Shell?
PHP2Shell — цепочка против движка PHP: давление на память, утечка в раздутом cookie PHPSESSID, повторное давление и запрос, который подстраивается под слитые адреса. В захвате 24 октября она доходила до /bin/sh с родителем воркера Apache.
CVE-2026-109618 какие версии PHP затрагивает и есть ли патч для 8.4.26?
Этот отчёт показывает эксплуатацию на PHP 8.4.26 и не публикует матрицу остальных веток. Записи согласуются с CVE-2026-109618. Номер исправленного релиза берите из бюллетеня разработчика PHP.
Как проверить, что PHP уже эксплуатируют?
Ищите связку: memory_limit на line 0, PHPSESSID около 819 200 байт, segmentation fault воркера без строки access log и дочерний sh, curl, wget или php с родителем httpd или php-fpm.
Почему при RCE в PHP нет строки в access log?
Финальный запрос стартует оболочку и затем роняет воркер по сигналу 11. Apache не успевает дописать обычную строку этой стадии.
Если на периметре PHP 8.4 под Apache или FPM, а сканер смотрит только код ответа, имеет смысл сверить версию интерпретатора и родителя процессов вручную. SecurityLab.pro закрывает это пентестом внешнего периметра: веб-воркеры, сессии и то, что access log не показывает.