06.03.2006

Защита Solaris от руткитов, часть первая

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

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

В этой статье я хочу рассмотреть метод защиты сисколов в операционной системе Solaris.

1.Подмена сискола

Сначала мне стало интересно, как же собственно происходит замена системного вызова. Ведь, подумал я, если при замене сискола вызываются какие-то специфичные функции(или функции с какими-нибудь специфичными параметрами), то можно следить за подменой сискола с помощью dtrace. Для этого я сделал модуль, который содержит всего три строчки в функции _init()

_init(void) {
int i = modinstall(&modlinkage);

sysent[SYS_mkdir].sy_callc=(void *)0xfe8ecff4; /* fe8ecff4 - адрес SYS_mkdir.В вашей системе, скорее всего, адрес будет отличаться*/

return i;
}
Компилим, линкуем, загружаем
-bash-3.00#gcc  -D_KERNEL -DSVR4 -DSOL2  -c mdb_lkm.c -o mdb_lkm.o && ld -r mdb_lkm.o -o mdb_lkm 
-bash-3.00#modload mdb_lkm
-bash-3.00#modinfo
174 f9dec620    a80 156   1  profile (Profile Interrupt Tracing)
175 f9e07620   1744 159   1  sdt (Statically Defined Tracing)
176 feba0bd2    4c4 157   1  systrace (System Call Tracing)
177 f9ed8f39     d8   -   1  mdb_lkm (mod)
-bash-3.00#
Берем адрес(f9ed8f39) и в mdb
-bash-3.00# mdb -k
Loading modules: [ unix krtld genunix specfs dtrace ufs ip sctp usba uhci random fctl nca lofs crypto nfs audiosup sppp ptm ipc ]
> f9ed8f39::dis
0xf9ed8f39:                     addb   %al,(%eax)
0xf9ed8f3b:                     addb   %dl,-0x77(%ebp)
mdb_lkm`_init+2:                inl    $0x83
mdb_lkm`_init+4:                inb    (%dx)
mdb_lkm`_init+5:                orb    %al,0xe0680cec(%ebx)
mdb_lkm`_init+0xb:              outl   (%dx)
mdb_lkm`_init+0xc:              pushfl
mdb_lkm`_init+0xd:              aam
mdb_lkm`_init+0xf:              movl   $0x8304abc8,%ebx
mdb_lkm`_init+0x14:             les    (%eax),%edx
mdb_lkm`_init+0x16:             movl   %eax,-0x4(%ebp)
> mdb_lkm`_init+0x16
mdb_lkm`_init:                  pushl  %ebp
mdb_lkm`_init+1:                movl   %esp,%ebp
mdb_lkm`_init+3:                subl   $0x8,%esp
mdb_lkm`_init+6:                subl   $0xc,%esp
mdb_lkm`_init+9:                pushl  $0xd49cefe0
mdb_lkm`_init+0xe:              call   +0x4abc8c0       
mdb_lkm`_init+0x13:             addl   $0x10,%esp
mdb_lkm`_init+0x16:             movl   %eax,-0x4(%ebp)
mdb_lkm`_init+0x19:             movl   $0xfe8ecff4,0xfec4961c
mdb_lkm`_init+0x23:             movl   -0x4(%ebp),%eax
mdb_lkm`_init+0x26:             leave
mdb_lkm`_init+0x27:             ret
Все то что выше _init+0xe нас не интересует - это относится к mod_install. Нас интересует строчка
   movl   $0xfe8ecff4,0xfec4961c
Т.е видно, что заменить сискол можно простым movl'om. Причем заметьте, fe8ecff4 - адрес SYS_mkdir, т.е мы заменям сискол своим же адресом, а fec4961c - это адрес в таблице sysent
 
> fec4961c::dis
sysent+0x50c:                   hlt
sysent+0x50d:                   iret
sysent+0x50e:                   movw   %esi,
sysent+0x510:                   addl   (%eax),%eax
sysent+0x512:                   addb   %al,(%eax)
sysent+0x514:                   addb   %al,(%eax)
sysent+0x516:                   addb   %al,(%eax)
sysent+0x518:                   addb   %al,(%eax)
sysent+0x51a:                   addb   %al,(%eax)
sysent+0x51c:                   daa
sysent+0x51d:                   addb   %cl,0x5fe(%ebp)
2.Защита от подмены сискола Итак, как же будет выглядеть защитный модуль? В принципе, достаточно лишь сравнивать текущие значения указателей и те значения, которые были сделаны раньше. В этом нам поможет dtrace. Я отобрал, на мой взгляд,14 самых подверженных замене в руткитах системных вызовов. Итак,скрипт который будет делать нам дамп адресов сисколов
#!/usr/sbin/dtrace -qs
:::BEGIN
{
printf("0x%p,\n",`sysent[3].sy_callc); /* SYS_read*/
printf("0x%p,\n",`sysent[4].sy_callc); /* SYS_write*/
printf("0x%p,\n",`sysent[5].sy_callc); /* SYS_open*/
printf("0x%p,\n",`sysent[15].sy_callc); /*SYS_chmod*/
printf("0x%p,\n",`sysent[16].sy_callc); /*SYS_chown*/
printf("0x%p,\n",`sysent[23].sy_callc); /*SYS_setuid*/
printf("0x%p,\n",`sysent[54].sy_callc); /*SYS_ioctl*/
printf("0x%p,\n",`sysent[59].sy_callc); /*SYS_execve*/
printf("0x%p,\n",`sysent[61].sy_callc); /*SYS_chroot*/
printf("0x%p,\n",`sysent[81].sy_callc); /*SYS_getdents*/
printf("0x%p,\n",`sysent[88].sy_callc); /*SYS_lstat*/
printf("0x%p,\n",`sysent[141].sy_callc); /*SYS_seteuid*/
printf("0x%p,\n",`sysent[216].sy_callc); /*SYS_lstat_64*/
printf("0x%p\n",`sysent[225].sy_callc); /*SYS_open_64*/

exit(0);
}
-bash-3.00# ./pointers.d
0xfe90a674,
0xfe90aad2,
0xfe8f4c89,
0xfe88fb65,
0xfe88fde6,
0xfe969979,
0xfe8d5397,
0xfe8ba20a,
0xfe88f833,
0xfe8d0027,
0xfe92c2a3,
0xfe969bdf,
0xfe92c6e2,
0xfe8f4ce4
-bash-3.00#
Теперь сам syscall guard
#include 
#include 
#include 
#include 
#include 
#include    //includes
#include 
#include 
#include 

#include 

#define DELAY_N 5    //количество секунд между проверками
#define SYSC_TO_CHECK 14 //количество сисколов для проверки

extern struct mod_ops mod_miscops; //Далее всякие структуры для корректной работы модуля

static struct modlmisc modlmisc =
{
    &mod_miscops,
    "Syscall checker",
};

static struct modlinkage modlinkage =
{
    MODREV_1,
    (void *) &modlmisc,
    NULL
};


void my_func(void);

unsigned long pointers[SYSC_TO_CHECK] //Массив с указателями Их нужно получить с помощью pointers.d и вставить сюда
 = {


//insert pointers here

};

int n_p[SYSC_TO_CHECK]={3,4,5,15,16,23,54,59,61,81,88,141,216,225};  // номера сисколов для проверки


char *sys_names[SYC_TO_CHECK] = { // названия сисколов для определенности
"SYS_read",
 "SYS_write",
 "SYS_open",
"SYS_chmod",
"SYS_chown",
"SYS_setuid",
"SYS_ioctl",
"SYS_execve",
"SYS_chroot",
"SYS_getdents",
"SYS_lstat",
"SYS_seteuid",
"SYS_lstat_64",
"SYS_open_64"
};


timeout_id_t time_id;

int _fini(void) {//Эта функция вызывается при выгрузке модуля
 int i;
untimeout(time_id);
    if ((i = mod_remove(&modlinkage)) != 0)  {
        cmn_err(CE_WARN, "Could not remove module\n");
} else {
cmn_err(CE_WARN,"ALERT!! Syscall Guard removed!!"); 
}

    return i;

}




int _init(void)//Эта функция вызывается при загрузке модуля
{
    int i;
(void)my_func();//собственно  функция проверки
    if ((i = mod_install(&modlinkage)) != 0)
	cmn_err(CE_WARN, "Could not install module\n");
    else
	cmn_err(CE_WARN, "Syscall guard successfully installed\n");//change this!!!
    
 
return i;

}



void restore_sysc(int k) {//функция восстаноления сисколов
sysent[n_p[k]].sy_callc=(void *)pointers[k]; 
cmn_err(CE_WARN,"Restored original value of %s",sys_names[k]); 
}
/ В этой функции идет проверка таблицы сисколов из массива n_p берется номер системного вызова подставляется в sysent и сравниваеться со значением из массива sys_names Если адрес сискола из массива не равен текущему адресу то вызывается функция restore_sysc для восстановления сискола /

void my_func() {//функция проверки

int k;

 

for(k=0;k<SYSC_TO_CHECK;k++) {

 

if(sysent[n_p[k]].sy_callc != (void *)pointers[k]) { cmn_err(CE_WARN,"%s was changed",sys_names[k]); restore_sysc(k);

}

}

функция таймаута иначе система начинает потреблять очень много ресурсов / k=0; time_id = timeout((void (*)(void *))my_func,NULL,drv_usectohz(1000000*DELAY_N));

int _info(struct modinfo *modinfop)
{
    return (mod_info(&modlinkage, modinfop));
}
Но стоит учитывать то,что хакер, имея возможность загрузить модуль, имеет доступ ко всей системе, а следовательно может просто выгрузить наш модуль и почистить логи. Поэтому какой бы искушенной подобного рода защита ни была, опытный взломщик скорее всего найдет способ ее обойти.

/* By zZz , rootteam.void.ru Thanks to Xarth,devoid */

или введите имя

CAPTCHA