17 октября 2026 года были зафиксированы попытки атаки с использованием PHP2Shell. Патч вышел 21 октября. Если отсчёт времени уязвимости начинается с момента раскрытия (disclosure), вы ошибаетесь на четыре дня ещё до того, как успели что-либо сделать.
В вашем процессе патчинга наверняка есть аккуратный таймлайн. Публикуется CVE, создаётся тикет, запускается таймер SLA, и команда оценивает себя относительно нормы — например, 14 дней для критических уязвимостей. Таймлайн выглядит логичным и строгим, потому что все согласны с тем, когда он начинается. А начинается он с момента раскрытия.
PHP2Shell публично нарушила это соглашение. Первая зарегистрированная попытка эксплуатации произошла до появления публичного CVE, до выхода патча и до того, как в вашей системе вообще мог появиться тикет. У вас есть два плохих варианта действий в этой ситуации. Вариант первый: продолжать отсчёт от момента раскрытия и смириться с тем, что ваши отчёты показывают окно уязвимости меньшим, чем оно было у злоумышленников на самом деле. Вариант второй: перестроить отсчёт вокруг даты, которую вы обычно не знаете — даты первой эксплуатации, и принять тот факт, что часть показателей уязвимости станет оценкой «не менее чем», а не точной цифрой. Второй вариант менее комфортен. Но это единственный честный подход.
Что произошло на самом деле: разбор по датам
PHP2Shell — это название, которое компания Wiz дала двум уязвимостям безопасности памяти в самом движке PHP: CVE-2026-109618 и CVE-2026-109619, каждая из которых получила оценку 9.2 по шкале CVSS v4.0. В случае успешной эксплуатации неаутентифицированный атакующий получает возможность удалённого выполнения кода (RCE) на сервере, обрабатывающем веб-запросы. Уязвимый код восходит к PHP 5.4 2012 года, а PHP используется примерно на 70% веб-сайтов с известным серверным языком, так что под угрозой оказалось даже минимальное приложение hello world на уязвимой версии. Wiz продемонстрировала рабочие атаки на актуальные релизы WordPress, Drupal и Joomla. Уязвимости находились в движке, на котором работают все три системы.
История обнаружения важна не меньше самой баги. Wiz нашла первую уязвимость 19 августа 2026 года, используя свои инструменты Atlas вместе с передовыми ИИ-моделями. По заявлению компании, один исследователь сделал за неделю то, что раньше требовало месяцев работы команды экспертов. Вторая фирма, Calif, работая в рамках инициативы OpenAI Patch the Planet, независимо обнаружила ту же первопричину ранее в августе. В итоге три независимые стороны сообщили об одной и той же базовой уязвимости команде безопасности PHP в период с 11 августа по 18 сентября. Перечитайте это ещё раз: три независимых обнаружения за пять недель уязвимости, которая пролежала в коде четырнадцать лет.
Затем скоординированная часть процесса перестала быть таковой. 17 октября, за четыре дня до официального раскрытия, ловушка для хакеров (honeypot) MadPot от Amazon зафиксировала попытку эксплуатации PHP2Shell. В Amazon прямо заявили, что неизвестно, как именно атакующий получил информацию об эксплойте. Идентификаторы CVE утекли 20 октября, что заставило сдвинуть релиз PHP на день раньше — на 21 октября. Исправления вышли в версиях 8.5.12, 8.4.27, 8.3.36 и 8.2.35. В течение нескольких часов после релиза случайно опубликовали видео с демонстрацией концепта (PoC), а один из дистрибьюторов выложил пропатченные бинарники раньше времени. К 24 октября было подтверждено выполнение команд атакующими на honeypot-серверах, а 26 октября CISA добавила обе уязвимости в свой каталог Known Exploited Vulnerabilities (KEV). Википедия, нидерландский госсервис DigiD и провайдер HR-систем BambooHR на этой неделе временно отключили свои системы в качестве меры предосторожности на время патчинга.
Пробел, о котором все молчат
Большинство разборов PHP2Shell сводятся к одному совету: патчите быстрее. Этот совет правильный, но недостаточный, так как он по умолчанию предполагает, что гонка начинается в момент выхода патча. Приведённый выше таймлайн показывает, что гонка началась раньше, причём дважды. Во-первых, обнаружение уязвимости больше не является редким событием, происходящим один раз, у одного аккуратного исследователя и под эмбарго. Когда три группы находят одну и ту же 14-летнюю багу за пять недель, повторное обнаружение становится базовым сценарием. И как минимум у одного злоумышленника был рабочий эксплойт до каких-либо анонсов. Во-вторых, сам патч — это карта. Сравнение исправленного кода со старым (patch diffing) изолирует изменения, и эти изменения указывают прямо на уязвимость. Раньше диффинг патчей был медленной работой узких специалистов. Теперь это задача, с которой отлично справляются ИИ-модели. Именно поэтому интервал между выходом фикса и появлением эксплойта в сети в последнее время измеряется часами, а не неделями.
Окно уязвимости не начинается тогда, когда вы узнаёте о баге. Оно начинается тогда, когда о ней узнаёт первый хакер, а об этой дате вы, скорее всего, узнаете позже — или не узнаете вовсе.
Пробел, который закрывает эта статья, касается учёта времени. Если эксплуатация опережает раскрытие, то два ваших стандартных показателя ошибочны в противоположных направлениях. Ваш SLA на патчинг, отсчитываемый от момента раскрытия, занижает реальное время, в течение которого вы были уязвимы. А окно разбора инцидентов (incident review), которое большинство команд открывает с даты раскрытия, начинается на четыре дня позже, чем нужно. В результате поиск по логам просто не захватывает те дни, когда происходили ранние попытки атак. Исправление второго показателя стоит вам всего лишь изменения одной даты в посковом запросе. Но большинство команд этого до сих пор не сделали.
Считайте время уязвимости с первого выстрела, а не с анонса
Вот небольшой инструмент на Python (stdlib), который выполняет расчёт обоими способами для тестового парка серверов. Он проверяет версию PHP на каждом хосте относительно исправленных релизов, а затем оценивает четыре популярные стратегии патчинга дважды: от даты раскрытия и от даты первой зарегистрированной попытки эксплуатации 17 октября.
#!/usr/bin/env python3
from datetime import date
FIRST_EXPLOITATION = date(2026, 10, 17) # Amazon MadPot honeypot attempt
PATCH_RELEASED = date(2026, 10, 21) # PHP 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
CISA_KEV = date(2026, 10, 26) # Known Exploited Vulnerabilities
FIXED = {"8.5": (8, 5, 12), "8.4": (8, 4, 27), "8.3": (8, 3, 36), "8.2": (8, 2, 35)}
def parse(version):
return tuple(int(p) for p in version.split("."))
def status(version):
branch = ".".join(version.split(".")[:2])
if branch not in FIXED:
return "AFFECTED, no fix (end of life branch)"
if parse(version) < FIXED[branch]:
return f"AFFECTED, patch to {'.'.join(map(str, FIXED[branch]))}"
return "patched"
FLEET = [
("web-01", "8.4.26"),
("web-02", "8.4.27"),
("blog-01", "8.3.30"),
("legacy-01", "8.1.33"),
("api-01", "8.5.12"),
("shop-01", "8.2.28"),
]
PLANS = [
("same day as the patch", date(2026, 10, 21)),
("48 hour critical SLA", date(2026, 10, 23)),
("weekly patch cycle", date(2026, 10, 28)),
("monthly patch cycle", date(2026, 11, 4)),
]
for host, version in FLEET:
print(host, version, status(version))
for name, patched in PLANS:
print(name,
(patched - PATCH_RELEASED).days, "days after disclosure,",
(patched - FIRST_EXPLOITATION).days, "days after first exploitation")
Запустите его на полном файле (версия, сохранённая с этим черновиком, также выводит таблицу парка серверов и дату KEV), и вы получите следующее:
Fleet check (PHP2Shell, CVE-2026-109618 and CVE-2026-109619)
web-01 php 8.4.26 AFFECTED, patch to 8.4.27
web-02 php 8.4.27 patched
blog-01 php 8.3.30 AFFECTED, patch to 8.3.36
legacy-01 php 8.1.33 AFFECTED, no fix (end of life branch)
api-01 php 8.5.12 patched
shop-01 php 8.2.28 AFFECTED, patch to 8.2.35
-> 4 of 6 hosts affected
Exposure per patch plan, counted two ways
first exploitation attempt recorded: 2026-10-17
patch released: 2026-10-21
CISA KEV listing: 2026-10-26
same day as the patch patched 2026-10-21: 0 days after disclosure, 4 days after first exploitation
48 hour critical SLA patched 2026-10-23: 2 days after disclosure, 6 days after first exploitation
weekly patch cycle patched 2026-10-28: 7 days after disclosure, 11 days after first exploitation
monthly patch cycle patched 2026-11-04: 14 days after disclosure, 18 days after first exploitation
The disclosure clock understates exposure by 4 days on every plan.
В этом выводе стоит обратить внимание на несколько вещей. Во-первых, месячный цикл, который выглядит приемлемым (14 дней) в отчёте по комплаенсу, превращается в 18 дней относительно даты реальной эксплуатации. И 18 — это нижняя граница, а не точный срок, поскольку 17 октября — это всего лишь первая попытка, которую случайно заметил honeypot. Во-вторых, хост с завершённым сроком поддержки (End of Life) — самая проблемная строка в таблице. Для него нет исправленной версии, поэтому никакой SLA, выраженный в днях на патчинг, закрыть его не сможет. Ему нужна изоляция, правило WAF или вывод из эксплуатации, а это совсем другие тикеты с другими ответственными. В-третьих, обратите внимание: даже идеальный план (патчинг в тот же день) несёт в себе 4 дня уязвимости, в которых команда никак не виновата. Эта часть окна — не провал производительности, а цена процесса раскрытия, соблюдения которого злоумышленники больше не гарантируют.
Для реального парка серверов данные о версиях на большинстве инфраструктур получаются одной командой:
ansible webservers -m shell -a 'php -v | head -1'
А проверка KEV, которая должна приоритезировать вашу очередь критических задач, должна быть автоматическим фидом, а не веб-страницей, на которую кто-то иногда заходит:
curl -s https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json \
| jq '.vulnerabilities[] | select(.cveID=="CVE-2026-109618") | .dateAdded'
Проводите поиск угроз (threat hunting) от самой ранней обоснованной даты
Весь процесс сквозной аналитики выглядит так:
first exploitation (known or estimated)
|
v
disclosure / patch release ----> patch diffing by attackers (hours)
|
v
your deployment completes -----> exposure ends for that host
|
v
hunt window: [first exploitation - safety margin, deployment date]
Первый этап — это выбор даты, а не инструмента. Когда выходит критическая уязвимость в широко используемом рантайме, устанавливайте начало поиска атак на самую раннюю дату эксплуатации из отчётов. Если в отчётах указано, что точная дата первой попытки неизвестна (как у Amazon в данном случае), отнимайте дополнительный запас времени. Для PHP2Shell поиск, начатый 21 октября, по определению пропустит зафиксированную попытку. Поиск с 17 октября зафиксирует её, но всё равно, скорее всего, стартует с задержкой.
Второй этап — сопоставление с инвентаризацией. Оценка уязвимости индивидуальна для каждого хоста и версии, поэтому проверка парка серверов должна запускаться по достоверным конфигурационным данным, включая PHP внутри контейнеров и тот единственный легаси-сервер, за который никто не отвечает. Сервисы, которые ушли в оффлайн на неделе раскрытия, по сути выбрали гарантированный плановый простой вместо неизвестного риска компрометации. Такой выбор возможен только в том случае, если вы заранее знаете, какие хосты уязвимы.
Третий этап — временные меры для хостов, которые нельзя пропатчить прямо сейчас. Виртуальный патчинг, правило WAF или фильтр на реверс-прокси, составленный по опубликованным рекомендациям (detection guidance), даёт время на деплой, не создавая иллюзии, что хост исправлен. Отслеживайте это как отдельное состояние в той же таблице. Хост за виртуальным патчем имеет статус mitigated (риск снижен). Он не пропатчен (patched), и в ваших отчётах эти два понятия никогда не должны подменять друг друга.
Где эта схема ломается
- Дата первой эксплуатации — это нижняя граница, о которой вы узнаёте с задержкой. Amazon перехватила попытку 17 октября благодаря своим honeypot-сетям. Если уязвимый сервис не покрыт ловушками, даты у вас не будет вовсе, и честный показатель времени уязвимости превратится в оценочный, о чём нужно явно указывать в отчётах.
- Повторное открытие уязвимостей работает в обе стороны. Тот же ИИ-инструментарий, который помог защитникам найти PHP2Shell, доступен кому угодно. Режим эмбарго теперь должен выдержать молчание не одного исследователя, а всех независимых специалистов, плюс утечки вроде слива CVE-идентификатора 20 октября.
- Из-за диффинга патчей незаметный (silent) патч больше не является безопасным. Выпуск фикса без публичного бюллетеня (advisory) лишь задерживает тикет у защитников, тогда как атакующему, имеющему на руках две бинарные сборки, никакой бюллетень не нужен.
- Ветки с истекшим сроком поддержки (EOL) ломают модель полностью. Никакие часы, даже самые честные, не помогут пропатчить рантайм, который никто не поддерживает. Для PHP 8.1 и старше в этом инциденте исправлений не было вообще.
- Отключение сервиса иногда бывает правильным решением, но это всегда расходы. Википедия и DigiD могли позволить себе профилактический простоя. Платежный шлюз в конце квартала — навряд ли. Именно поэтому такие решения должны опираться на данные инвентаризации до кризиса, а не во время него.
Внедрять или пропустить?
Внедряйте, если у вас используется любой доступный из интернета рантайм или библиотека с большой базой установок, а текущие отчёты считают время патчинга от даты раскрытия уязвимости. Экспресс-план на полдня: добавьте поле «первая дата эксплуатации» в шаблон критических CVE, по умолчанию ставьте туда дату раскрытия, перезаписывайте её, как только появляются надёжные данные о более ранних атаках, и настройте поисковые запросы по логам на чтение этого поля вместо даты создания тикета. Добавьте проверку KEV-фида в пайплайн формирования очереди на патчинг. На этом всё. Два поля и один фид.
Пропустите, если ваша инфраструктура настолько мала, что критические патчи гарантированно накатываются в тот же день на абсолютно все хосты, включая контейнеры и легаси-серверы. В этом случае ваш таймер раскрытия и таймер эксплуатации отличаются на дни, которые вы уже закрыли, и изменение учёта не даст практической пользы. Главное — убедитесь, что легаси-сервер действительно есть в инвентаре, прежде чем претендовать на это исключение. Обычно его там нет.
Запускайте таймер тогда, когда это сделал атакующий
Возьмите ваш самый свежий критический патч — тот, который на дашборде отмечен как закрытый вовремя. Пересчитайте сроки относительно самой ранней даты эксплуатации, которую сможете найти в бюллетене вендора, записи KEV или отчётах honeypot-систем, и сдвиньте начало поиска атак на эту дату минус запас времени. Затем перезапустите поиск по логам за эти образовавшиеся дни. Оцените изменения: насколько вырос показатель времени уязвимости и потребовалось ли дополнительное внимание к событиям из этих «выпавших» дней. На основе этих данных решите, жизнеспособен ли ваш текущий месячный цикл патчинга.
Когда ваша команда отчитывается об эффективности патчинга, с какой даты запускается отсчёт и кто в вашем процессе отвечает за его сдвиг на более ранний срок, если появляются соответствующие данные?
Комментарии (0)
Пока нет комментариев — будьте первым.