緩衝區溢位原理

緩衝區溢位原理

目的

指的是利用程式的漏洞,向緩衝衝區寫入使溢位的值,可用來達到以下目的:

  • 程序破壞
  • 控制程式流程
  • 取得系統的控制權

以下將透過編寫簡單的程式,並輸入過長字串讓程序當掉,以了解緩衝區溢位的原理。

環境介紹

  • OS:Windows XP_x86 (SP3)
  • IDE:VC++ 6.0

事前準備及利用緩衝區漏洞

這裡以C寫一段存在緩衝區漏洞的程式

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>

char name[] = "HAO-WEI";

int main() {
char buffer[8];
strcpy(buffer, name);
printf("%s \n", buffer);

return 0;
}

這段程式是先宣告name變數資料型態為Char的陣列,並設值為”HAO-WEI”,
並在主程式中,宣告buffer變數一樣為Char的陣列的資料型態,設定與name變數相同長度,因為HAO-WEI後面還有一個’\0′
使用strcpy函數將name的字串複製給buffer,最後再輸出。

以上是有存在緩衝區溢位的程式碼,這邊思考一下,如果name的長度比8更長,或是buffer長度比8小,讓他真的發生緩衝區溢位,那麼會發生什麼事呢?
這邊將name內容設為HAO-WEIHAO-WEI,長度為15,並編譯執行

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>

char name[] = "HAO-WEI";

int main() {
char buffer[8];
strcpy(buffer, name);
printf("%s \n", buffer);

return 0;
}

程序雖然成功的輸出HAO-WEIHAO-WEI,但隨即就當掉了。
緩衝區不就只是滿了,為什麼會發生這樣的事情呢?

分析說明

要解釋這個問題時,先講解程式在執行時Stack運作原理
程式在直行時會有一塊記憶體是存放Stack的部分
當進入副程式時,系統會給予這副程式一塊可用記憶體,稱為Stack Frame(如下圖)

其中epb跟esp都是暫存器的值

  • epb表示Stack底端
  • esp表示Stack頂端

每一個Stack Frame都有屬於自己的esp跟ebp
所以只要把epb的值+4就可以拿到這個Function的變數2的值
epb的值+8也就可以拿到變數1的值
而最上面綠色那塊則是這Function可用的其他記憶體空間
另外也會把這個Function完成後
等等要執行下一個指令的位置寫到Return Address這裡
也會把呼叫這個Function的舊ebp保存起來
以供結束此Function能夠回復上一個Function的ebp
這樣Function一直不斷呼叫,記憶體不斷的往上疊,也不會亂掉,因為每個Function只需在意他自己的ebp跟esp

這邊用OllyDbg來進行動態分析,以了解程式的組合語言以及執行時的Stack變化

進入FUNCTION前

我們先把程式停在進入Main()這個Function前
另外也需特別注意下一個指令的位置00401699
結束完Main()時會繼續往這裡走,所以需要把這個位置寫到Stack中(等等下面圖片會看到)

1
00401694        CALL BufferOv.00401005 // 表示CALL Main()

以及他的Stack狀況

可以知道目前esp為0012FF88

進入FUNCTION後

觀察一下Stack狀態
就可以發現目前的esp變為0012FF24
而ebp為0012FF80 其值為0012FFC0(上一個Function的ebp)
而0012FF84(ebp + 4) 其值為00401699(為Main()執行完要回去的位置,也稱Return Address)
而這個Main()所用記憶體也就是0012FF24 ~ 0012FF7C這些空間

執行STRCPY前

我們來看一下Main()裡面寫到的strcpy這Function的C語言以及其對應組合語言的部分

1
strcpy(buffer, name);

strcpy的API

1
char * strcpy ( char * destination, const char * source );

可以知道他把”HAO-WEI”這些字串拷貝到[ebp – 8]也就是0012FF78這個位置上

執行STRCPY後

我們來看看0012FF78這個位置的值發生什麼變化

已經被填入”HAO-WEI”的字串了,看起來沒什麼問題
那如果是填入過長字串如”HAO-WEIHAO-WEI”會怎樣呢?我們來看看

結果發現0012FF84原本放Main()執行完要去的位置被覆寫掉了,變成00004945
意謂著等等結束Main()會跳到00004945位置繼續執行指令,而這個位置如果是空的,程式自然就會當掉,發生以下情形

很明顯的圖片上出現的0x00004945就是這個意思

最後

換個角度想,如果覆寫掉的這個位置是存在的,就樣是不是就能控制程式流程,隨心所欲的跳到想要的位置呢?