На внутреннем пентесте в 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, который наступает после получения начального доступа и закрепления. В типовой цепочке внутреннего пентеста:
- Initial Access - компрометация учётных данных или эксплуатация уязвимости сервиса
- Privilege Escalation - получение root через kernel exploit, misconfiguration или SUID-бинарь
- Persistence - установка C2-агента (systemd-сервис, cron, authorized_keys)
- Defense Evasion (текущий шаг) - сокрытие процесса агента от мониторинга
- Credential Access / Lateral Movement - сбор учётных данных, движение по сети
modules_disabled=1. Часто идёт в связке с зачисткой логов - T1070, Indicator Removal.Логика атакующего тут простая: если SOC видит подозрительный процесс - инцидент стартует, доступ теряется. Если процесс невидим для стандартных инструментов - остаётся время на exfiltration, lateral movement или развёртывание ransomware.
Как /proc filesystem экспонирует процессы в Linux
Ядро 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 техника
Предусловия:
- Работает если: 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: дополнительных ресурсов не требуется
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 aux | PID отсутствует (не читает /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напрямую
Linux forensics расследование: обнаружение скрытых процессов
Анализ mountinfo для обнаружения руткита Linux
Самый прямой способ обнаружить bind mount поверх/proc/<PID> - анализ файла mountinfo. Каждый mount namespace ведёт свой файл /proc/self/mountinfo, где пятое поле содержит точку монтирования.
Bash:
awk '$5 ~ /^\/proc\/[0-9]+$/ {print}' /proc/self/mountinfo
/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
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 сценарий целиком.
Последнее редактирование модератором: