user@elrise.io:~/2026-08-09-elrise-backend-container-compromise
· [critical] incident-reportphp-fpmazazelphp8.5

elrise-backend container compromise: postmortem за 18 минут от 502 до docker stop

Все имена хостов, баз, топиков и учётные данные в примерах, кроме явно помеченных как реальные, — вымышленные или placeholder'ы. Совпадения с реальными системами случайны.


TL;DR

Severity: critical — активный криптомайнер/backdoor, контейнер имел outbound + filesystem write capability через :rw bind mount.

Detected: 2026-08-09 ~18:30 UTC, при проверке 502 на https://elrise.ru/ и https://elrise.io/.

Reporter: дежурный инженер (operator-triggered).

Status: скомпрометированный стек остановлен; SSH закалён; forensics и recovery в работе.

Восемнадцать минут — от операторской жалобы до docker stop. Семьдесят два часа до этого rootkit жил в контейнере незамеченным. Эта публикация — хронология инцидента, разбор fingerprint-ов компрометации, и почему следующий такой инцидент должен ловиться не через оператора, а через алерт. Recovery plan намеренно не включён — это отдельный internal runbook.


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

Контейнер elrise-backend (PHP-FPM 8.5 production-стек elrise-symfony, запущенный на tmp-vps 46.36.219.176) был скомпрометирован атакующим, который:

  1. Сбросил backdoor toolkit в /tmp/.r.rpk/, /tmp/.xdiag/, /tmp/.apid, /tmp/.perf.c/
  2. Запустил второй php-fpm master-процесс (rogue, PID 1397, отличимый от легитимного PID 1 отсутствием : в имени процесса)
  3. Запустил apache2 -DFOREGROUND worker под www-data с именем процесса, замаскированным под {xzdiffunlink} через prctl(PR_SET_NAME, ...) — подозрительно, потому что оригинальный образ elrise-symfony не устанавливает apache2
  4. Запустил BusyBox shell backdoor sh ybox с 6 открытыми файловыми дескрипторами (интерактивный)
  5. Запустил sleep 300; rm -rf /tmp/.install.pid* (классический паттерн drop+cleanup криптомайнера — staging payload, sleep для уклонения от детекции, потом удаление installation-артефактов)
  6. Эксплуатировал CVE-2012-1823 (PHP-CGI argv injection) через POST /usr/local/lib/php/PEAR.php с source IP 211.234.111.116 — запрос был обработан (HTTP 200), что означает, что backdoor принял attack-трафик

Немедленные операторские действия (под явным операторским одобрением):


2. Timeline (UTC)

Time Событие
До 2026-08-06 Образ elrise-symfony:local собран из <project-root>/Dockerfile (последняя правка Dockerfile: Aug 6 01:02 per ls -la)
2026-08-06 18:59 /tmp/.r.rpk/ впервые создан (20 entries) — самое раннее forensic-свидетельство компрометации
2026-08-09 17:43 Сервер перезагружен (load average 47 / 1 CPU; elrise-backend показывал 82% CPU до перезагрузки по данным предыдущего аудита)
2026-08-09 17:44 Контейнер elrise-proxy запущен (по docker logs)
2026-08-09 17:55 elrise-proxy graceful shutdown (SIGQUIT)
2026-08-09 17:56:33 elrise-backend запущен (после FastPanel-initiated restart) — тот же контейнер, который был скомпрометирован до перезагрузки, теперь работает с malware
2026-08-09 18:28:36 Первая наблюдаемая атака с 211.234.111.116: POST /usr/local/lib/php/PEAR.php → HTTP 200 (CVE-2012-1823)
2026-08-09 18:28:43 Follow-up probe: POST /usr/share/php/PEAR.php → HTTP 404
2026-08-09 18:30 Оператор открывает браузер, видит 502 на apex-доменах, запрашивает проверку
2026-08-09 ~18:33 Дежурный инженер обнаруживает apache2 -DFOREGROUND + rogue php-fpm в ps aux; классифицирует как компрометацию
2026-08-09 ~18:38 Оператор одобряет: «стопай контейнеры, выключай авторизацию по паролю»
2026-08-09 ~18:38 docker stop elrise-backend elrise-proxy выполнен; контейнеры остановлены
2026-08-09 ~18:39 PasswordAuthentication no применён; sshd перезагружен; SSH key-only auth подтверждён

3. Evidence

3.1 Таблица процессов (внутри elrise-backend, захвачена в 18:31 UTC до остановки)

PID   USER     TIME  COMMAND
    1 root      0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
   12 www-data  0:02 php-fpm: pool www
   13 www-data  0:04 php-fpm: pool www
 1334 www-data  0:00 sh -c sleep 300;rm -rf /tmp/.install.pid* 2>/dev/null
 1336 www-data  0:00 sleep 300
 1393 www-data  0:00 {busybox} sh ybox
 1397 www-data  0:14 php-fpm master process /usr/local/etc/php-fpm.conf
 1605 www-data  3:32 {xzdiffunlink} apache2 -DFOREGROUND

Аномалии:

3.2 /tmp/ filesystem (writable by www-data)

/tmp/.apid               Aug  9 18:29   4 bytes     marker file (4-byte payload)
/tmp/.perf.c/            Aug  9 18:29   dir         source code for native compilation
/tmp/.r.rpk/             Aug  6 18:59   20 entries  malware staging (earliest evidence)
/tmp/.xdiag/             Aug  9 18:31   7 entries   runtime diagnostics / backdoor files
/tmp/sess_*              20+ files      PHP session files (legitimate PHP-FPM behaviour)

Каталог .r.rpk особенно подозрителен: 20 entries созданы 5 дней назад (во время pre-reboot high-load периода), что предполагает, что атакующий имел persistent access через предыдущий server uptime.

3.3 Active attack traffic (access log)

211.234.111.116 - "POST /usr/local/lib/php/PEAR.php"   200    ← CVE-2012-1823 (PHP-CGI argv injection)
211.234.111.116 - "POST /usr/share/php/PEAR.php"     404    ← follow-up probe
172.25.0.2    - "GET /index.php"                     200    ← internal healthcheck from elrise-proxy

211.234.111.116 — source IP атакующего. HTTP 200 в ответ на POST /PEAR.php — самый тревожный индикатор: PHP-FPM интерпретировал /usr/local/lib/php/PEAR.php как путь к PHP-скрипту и выполнил его. Это вектор CVE-2012-1823 (PHP-CGI mode) — современный PHP-FPM НЕ ДОЛЖЕН принимать это, потому что cgi.fix_pathinfo выключен по умолчанию в PHP 8+. То, что вернулся 200, означает одно из:

(Последние две гипотезы наиболее вероятны с учётом наличия apache2.)

3.4 Bind-mount attack surface (verified writable из контейнера)

/app/.env test: WRITABLE  (uid 1005 = deploy on host = <project-root>/*)

compose.yaml биндит ./:/app:rw — это требуется для dev override flow по ADR-0005. Но это также означает, что атакующий внутри контейнера мог писать в host-файлы под <project-root>/ с теми же правами, что у deploy на хосте. Свидетельств host-side tampering нет (mtime файлов <project-root>/{Dockerfile,.env,.env.local} не изменились, все от Aug 4-6), но возможность существовала.

3.5 Network exposure (assumed)


4. Vector analysis (entry point — speculative, pending forensics)

Наиболее вероятно: CVE-2012-1823 (PHP-CGI argv injection) через POST /PEAR.php — активность с 211.234.111.116 в 18:28:36 UTC подтверждена. PHP 8.5 + FPM НЕ ДОЛЖЕН быть уязвим по умолчанию, но:

Другие векторы (менее вероятные, но возможные):


5. Affected systems

Система Статус Замечание
Контейнер elrise-backend Stopped (operator-approved) Активный malware остановлен
Контейнер elrise-proxy Stopped (operator-approved) Fronting proxy, потенциально тоже скомпрометирован
<project-root>/* host-файлы Untouched (пока нет свидетельств tampering, но writable через bind mount — требует forensic review) <project-root>/.git/ MUST быть проверен
/var/log/btmp, sshd Hardened (Method A применён, password auth off) Brute-force-кампания продолжается на IP/transport-уровне, но приземлиться не может
Внешний HTTPS на elrise.ru / elrise.io 502 / parking page (ожидаемо) Восстановится при redeploy elrise-backend с patched image

6. Lessons learned

  1. PHP-FPM pm.max_children=5 — это footgun. Один медленный запрос насытил контейнер (наблюдение 18:30 UTC: elrise-backend на 188% CPU, 2.5 GB RAM, запросы в очереди 30+ секунд). Рекомендуется увеличить pm.max_children с 5 до 15+ в пересобранном образе.

  2. Bind-mount :rw — это attack surface. /app/.env WRITABLE из контейнера означало, что атакующий мог модифицировать host-файлы. Dev override flow требует более ограниченного mount'а (:ro для runtime-контейнера, с отдельным :rw mount только в dev-mode compose override).

  3. Process name masking через prctl(PR_SET_NAME) невидим в ps -ef (видно только в /proc/<pid>/comm через cat). Соответствующий stuck-process-reaper daemon должен сканировать comm-поля на подозрительные имена (короткие opaque строки, имена с embedded {...} скобками).

  4. Нет forensic baseline. У нас не было «known-good» image snapshot для сравнения. Рекомендуется поддерживать <project-root>/Dockerfile.sha256 и cron-driven sha256sum всех бинарников в /usr/local/bin/, /usr/bin/, /usr/sbin/ работающего контейнера, с алертами на drift.

  5. Расследование 18:30 UTC было operator-triggered, не автоматическим. Никаких алертов при 188% CPU на контейнере, никаких алертов на новый apache2 процесс, никаких алертов на /tmp/.r.rpk/ (который лежал 5 дней незамеченным). Trio из memory-pressure-watcher + php-fpm-guard + healthcheck-relay daemon'ов закрывает этот пробел; без них следующая компрометация тоже будет идти незамеченной днями.

  6. 502 от PHP-FPM контейнера — это подозрительно, не просто «медленно». FastPanel по умолчанию отвечает 504 (gateway timeout), когда upstream не отвечает, но elrise.io/elrise.ru отдали 502 — это значит PHP-FPM принимал соединения, но отвечал мусором или пустотой (malware-state). Стоит исследовать 502 от PHP-FPM-backed сервиса агрессивнее, чем 504.


7. Что осталось открытым после инцидента

Публичный пост не место для internal TODO-list, но есть смысл обозначить границы того, что мы знаем сейчас и что ещё предстоит выяснить:

  1. Глубина forensics: полный snapshot + offline-анализ (отдельный внутренний процесс) или только достаточно для подтверждения entry-point + ротации секретов + redeploy?
  2. Стратегия image rebuild: пересборка из чистого source через docker compose build в <project-root>/ (операторски), или сначала задача для engineering (root-cause + remediation plan)?
  3. Мониторинг rollout: установить предложенных daemon'ов (php-fpm-guard, memory-pressure-watcher, healthcheck-relay) на tmp-vps немедленно или после стабилизации redeploy?
  4. Lateral-movement audit scope: только elrise-symfony или sweep всех контейнеров (converter-*, umami-app, artgs-symfony, artgs-redis)?
  5. CVE-2012-1823 конкретно: почему POST /PEAR.php вернул 200? Требует либо cgi.fix_pathinfo=1 в PHP-FPM, либо reverse proxy (rogue apache2), ре-вводящий cgi-style handling. Нужно подтвердить, какой именно.

8. Sources