Статья Скрытие процессов Linux через bind mount: от атаки до forensics-обнаружения

Тёмная лаборатория с изогнутым монитором, на экране которого светятся команды монтирования процессов в терминале. Красная схема дерева процессов с зачёркнутым узлом тонет в полумраке.


На внутреннем пентесте в 2024 году после эскалации до root на Ubuntu 22.04 через кривой sudoers нужно было удержать C2-агент незамеченным на production-сервере. Одна команда mount --bind поверх /proc/<PID> - и процесс исчез из ps, top, htop и lsof. SOC заказчика не обнаружил агент 48 часов при работающем мониторинге. Ни компиляции LKM-модуля, ни записей в dmesg, ни модификации ядра - только root и штатная утилита mount, которая есть в любом дистрибутиве. Красота и ужас одновременно.

Место в kill chain: сокрытие следов Linux после получения root​

[Применимо: внутренний пентест, post-exploitation фаза, Linux-серверы без LSM в enforcing-режиме] Подробнее - в нашем статье о техники руткитов классификация.

Скрытие процессов Linux - шаг Defense Evasion, который наступает после получения начального доступа и закрепления. В типовой цепочке внутреннего пентеста:
  1. Initial Access - компрометация учётных данных или эксплуатация уязвимости сервиса
  2. Privilege Escalation - получение root через kernel exploit, misconfiguration или SUID-бинарь
  3. Persistence - установка C2-агента (systemd-сервис, cron, authorized_keys)
  4. Defense Evasion (текущий шаг) - сокрытие процесса агента от мониторинга
  5. Credential Access / Lateral Movement - сбор учётных данных, движение по сети
По классификации MITRE ATT&CK техника bind mount прямо маппится на T1564.013 (Bind Mounts), тактика Defense Evasion. На практике она работает как userland-альтернатива полноценному LKM-руткиту (T1014, Rootkit): не нужно компилировать модуль ядра, нет алерта при загрузке модуля, работает на ядрах с modules_disabled=1. Часто идёт в связке с зачисткой логов - T1070, Indicator Removal.

Логика атакующего тут простая: если SOC видит подозрительный процесс - инцидент стартует, доступ теряется. Если процесс невидим для стандартных инструментов - остаётся время на exfiltration, lateral movement или развёртывание ransomware.

Как /proc filesystem экспонирует процессы в Linux​

1784710485063.webp

Ядро Linux выставляет информацию о каждом запущенном процессе через виртуальную файловую систему procfs, смонтированную в /proc. Для процесса с PID 31337 существует каталог /proc/31337/ с псевдофайлами: stat (состояние), status (читаемая сводка), cmdline (командная строка запуска), maps (карта памяти), fd/ (открытые файловые дескрипторы) и десятки других.

Все стандартные утилиты мониторинга работают по одному алгоритму: readdir() на /proc/, фильтрация числовых каталогов, чтение /proc/<PID>/stat для получения метаданных. Это касается ps, top, htop, pgrep, а также агентов вроде osquery, Elastic Agent (Auditbeat), Prometheus node_exporter. По сути /proc - единственный источник правды о процессах для userland на GNU/Linux. И вот тут собака зарыта.

Архитектурная слабость: /proc/<PID>/ - точка монтирования в Virtual File System (VFS). Ядро спокойно разрешает монтировать поверх неё другую файловую систему или пустой каталог через bind mount. После такой операции VFS-путь /proc/<PID>/ указывает на содержимое новой точки монтирования, а оригинальные псевдофайлы процесса становятся недоступны. Одна команда - и весь userland-мониторинг ослеплён.

hidepid и mount namespace Linux: что защищают и что нет​

Опция hidepid при монтировании procfs (mount -o remount,hidepid=2 /proc) ограничивает видимость /proc/<PID>/ для непривилегированных пользователей: с hidepid=2 обычный пользователь видит только собственные процессы. Но вот в чём подвох: hidepid - опция монтирования procfs, влияющая исключительно на непривилегированных пользователей. На root и операции монтирования она не действует. Root с CAP_SYS_ADMIN выполняет bind mount поверх любого каталога в /proc/ вне зависимости от значения hidepid. Так что если у атакующего уже root - hidepid ему не помеха.

Отдельный нюанс - mount namespace. GNU/Linux поддерживает изоляцию пространств монтирования через unshare(CLONE_NEWNS). Запись о bind mount видна только в том mount namespace, где он был выполнен. Для forensics это критично: проверка /proc/self/mountinfo показывает монтирования только в пространстве текущего процесса. Для полного покрытия нужно проверять /proc/*/mountinfo для всех PID - каждый файл mountinfo отражает mount namespace соответствующего процесса.

Скрыть процесс Linux: bind mount как Linux rootkit техника​

1784710520536.webp

Предусловия:
  • Работает если: root-доступ (UID 0) или capability CAP_SYS_ADMIN; ядро Linux 2.4+; SELinux/AppArmor в permissive-режиме или без профиля, ограничивающего mount на /proc
  • Не работает если: SELinux в enforcing с политикой, запрещающей mount на procfs; AppArmor с кастомным профилем, блокирующим bind mount; контейнер без привилегий (Docker/LXC - mount запрещён по умолчанию)
Требования к окружению:
  • ОС: любой дистрибутив GNU/Linux с ядром 2.4+ (Ubuntu 18.04+, CentOS 7+, Debian 9+, RHEL 7+)
  • Привилегии: root или CAP_SYS_ADMIN
  • RAM/CPU: дополнительных ресурсов не требуется
Допустим, C2-агент работает с PID 31337. Атакующий выполняет:
Bash:
mkdir -p /tmp/.x
mount --bind /tmp/.x /proc/31337
Каталог /proc/31337/ физически остаётся в листинге /proc/, но его содержимое теперь - пустая директория /tmp/.x. Псевдофайлы stat, status, cmdline, environ, maps, подкаталоги fd/, ns/, net/ - всё недоступно через стандартные пути. Две строчки - и процесс как будто испарился.

Результат для инструментов мониторинга:

ИнструментПоведение после bind mount
ps auxPID отсутствует (не читает /proc/<PID>/stat)
top, htopПроцесс не отображается
pgrep, pidofНе находят процесс
ls /proc/<PID>/Пустой каталог (не ошибка, а пустота)
lsofНе видит файловые дескрипторы процесса
osquery (process_events)Не включает процесс в инвентарь

Процесс при этом продолжает работать: его task_struct существует в ядре, он получает CPU-время, держит сетевые сокеты, пишет в файлы. Ядро "знает" о процессе - просто VFS-путь к метаданным перекрыт.

Что увидит и что пропустит SOC​

На стороне защиты возникает характерный индикатор: утилита ss -tnlp может показать сетевой сокет без ассоциированного PID (отображается - вместо имени процесса). Происходит это потому, что ss читает сокеты через netlink, а затем ищет владельца через /proc/*/fd/ - и не находит, так как /proc/31337/fd/ скрыт bind mount. "Осиротевший" сокет с активным сетевым соединением - серьёзный красный флаг для внимательного аналитика. Но надо ещё знать, что искать.

Агенты мониторинга реагируют по-разному, и тут важна вендор-специфика:
  • Elastic Agent 8.x+ на основе Auditbeat в конфигурации по умолчанию опрашивает /proc для получения списка процессов - bind mount полностью ослепляет его
  • CrowdStrike Falcon Sensor for Linux использует eBPF-зонды для части telemetry и может зафиксировать сам вызов mount(2)
  • SentinelOne for Linux в зависимости от версии агента сочетает kernel-модуль и userland-компоненты - bind mount на /proc не скрывает процесс от kernel-модуля, если тот читает task_struct напрямую
Разница принципиальная: userland-агенты слепнут, kernel-level - нет.

Linux forensics расследование: обнаружение скрытых процессов​

1784710560106.webp

Анализ mountinfo для обнаружения руткита Linux​

Самый прямой способ обнаружить bind mount поверх /proc/<PID> - анализ файла mountinfo. Каждый mount namespace ведёт свой файл /proc/self/mountinfo, где пятое поле содержит точку монтирования.
Bash:
awk '$5 ~ /^\/proc\/[0-9]+$/ {print}' /proc/self/mountinfo
Если вывод непустой - в системе есть bind mount поверх /proc/<PID>. В штатной конфигурации GNU/Linux ничего не монтирует поверх отдельных PID-каталогов в procfs, поэтому любая запись вида /proc/<число> в пятом поле mountinfo - практически гарантированный индикатор сокрытия процесса. Ложных срабатываний я на практике не встречал.

Но есть нюанс: эта проверка видит монтирования только в mount namespace текущего процесса. Если атакующий использовал nsenter или unshare --mount, запись может отсутствовать в вашем namespace. Для полного покрытия итерируйте mountinfo по всем PID:
Bash:
for pid in /proc/[0-9]*/; do
  awk '$5 ~ /^\/proc\/[0-9]+$/ {print FILENAME": "$0}' "$pid/mountinfo" 2>/dev/null
done
Это покажет монтирования в каждом mount namespace, представленном в системе.

PID busting: forensics анализ процессов Linux​

Метод, который команда Sandfly Security описывает как "PID busting", строится на сравнении того, что показывает /proc, с результатами прямых системных вызовов. Системный вызов kill(pid, 0) проверяет существование процесса, минуя VFS - ядро обращается к task_struct напрямую. Если kill -0 <PID> возвращает 0 (процесс существует), но /proc/<PID>/stat недоступен - процесс скрыт.

На практике: перебираются PID от 1 до значения /proc/sys/kernel/pid_max (по умолчанию 4194304 на современных ядрах, 32768 на legacy-системах). Для каждого PID вызывается kill -0 и проверяется доступность /proc/<PID>/stat. Расхождение = скрытый процесс. Грубо, но надёжно.

Утилита unhide (есть в репозиториях большинства дистрибутивов) автоматизирует этот подход: команда unhide procall сравнивает данные /proc с информацией от /bin/ps и системных вызовов. Sandfly Security предоставляет утилиту sandfly-processdecloak - статически скомпилированный бинарь без зависимостей от системных библиотек. Это критично при расследовании инцидента: shared-библиотеки на скомпрометированном сервере могут быть подменены, и тащить туда динамически слинкованный бинарь - так себе идея.

Memory forensics и дополнительные индикаторы​

Bind mount работает исключительно на уровне VFS - он не модифицирует ядро и не патчит syscall table. Дамп оперативной памяти содержит полную информацию обо всех процессах, включая скрытые. VFS-трюки ядру безразличны - task_struct на месте.

Для создания дампа на работающей GNU/Linux-системе используется модуль LiME (Linux Memory Extractor), загружаемый через insmod. Полученный дамп анализируется в Volatility 3 с плагинами linux.pslist (перечисление task_struct из ядра) и linux.pstree (дерево процессов). Скрытый bind mount процесс будет виден в дампе, потому что Volatility читает kernel structures напрямую, минуя VFS.

Дополнительные индикаторы на живой системе без memory dump:
  • Файл /proc/loadavg содержит общее число процессов - расхождение с ps aux | wc -l указывает на скрытые процессы
  • Вызов sysctl kernel.threads-cur покажет текущее число потоков в системе - сравнение с ps -eLf | wc -l выявляет аномалию
  • Правила auditd на syscall mount(2) с фильтром по пути /proc фиксируют момент создания bind mount, но только если auditd был настроен до атаки и не деактивирован атакующим - техника T1562.012 (Disable or Modify Linux Audit System) применяется именно для этого

Ограничения техники: когда bind mount не работает​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме


По данным исследования "Trace of the Times: Rootkit Detection through Temporal Anomalies in Kernel Activity" (arxiv, 2025), подходы на основе eBPF-трассировки ядра обнаруживают аномалии в рантайме системных вызовов с F1-score 98.7% по пяти сценариям. Исследование фокусируется на LKM-руткитах, но принцип eBPF-мониторинга распространяется и на userland-техники: kprobe на do_mount или path_mount перехватит вызов bind mount до завершения.

Итог: в средах с eBPF-мониторингом (Falco, Tetragon, Falcon Sensor) или kernel-модульными агентами (SentinelOne) bind mount на /proc будет детектирован. В средах без eBPF, без auditd и с userland-only мониторингом - техника остаётся рабочей. А таких сред, к сожалению, большинство.

Bind mount на /proc/<PID> - техника, к которой я отношусь с профессиональным уважением и одновременно с тревогой. Она работает именно потому, что эксплуатирует фундаментальное свойство GNU/Linux: procfs - единственный userland-интерфейс к информации о процессах. Пока индустрия мониторинга строится на чтении /proc, одна команда от root ломает всю видимость. На большинстве внутренних пентестов в RU-сегменте я вижу одну и ту же картину: auditd либо не настроен на отслеживание mount-операций, либо отсутствует вообще. eBPF-решения для GNU/Linux-серверов ещё не стали стандартом. Проверка mountinfo на аномальные записи не входит ни в один виденный мной runbook SOC-команды. При этом исправление элементарно - один скрипт с awk по mountinfo и перебор PID через kill -0, отправляющий алерт в SIEM, закрывает вектор полностью. Пока этого нет - техника bind mount остаётся одной из самых тихих в арсенале post-exploitation. Если хочешь отработать эту цепочку в контролируемой среде - на HackerLab.pro (https://hackerlab.pro) есть GNU/Linux-таски, где можно собрать post-exploitation сценарий целиком.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab