本篇以 C 語言的實際範例,解釋緩衝區溢位攻擊的原理。
緩衝區溢位攻擊是一種典型的攻擊方式,雖然這種手法已經有一段的歷史,不過還是有可能因為程式設計者的疏忽,讓程式存在這樣的漏洞。
程式中的緩衝區是指一塊特定的記憶體空間,程式在執行時可以將資料處存在其中,而緩衝區溢位(buffer overflow)就是指程式將資料寫入緩衝區時,超過了它的範圍,例如:
char buff[4]; buff[5] = 'a';
這裡我們宣告的 buff
字元陣列長度只有 4
,是我們卻將資料寫入第六個元素的位置,超過了 buff
字元陣列的範圍。
上面那種緩衝區溢位是屬於 stack 的溢位,還有另外一種類型是 heap 的溢位:
char *ptr = (char*) malloc(4); ptr[5] = 'a';
兩種溢位概念上差不多,只是差在些入的記憶體是在 stack 還是 heap 而已。
#include <stdio.h> #include <string.h> int main() { char buff[10]; int pass = 0; printf("Enter the password:\n"); gets(buff); if(strcmp(buff, "gtwang")) { printf("Fail.\n"); } else { printf("Pass.\n"); pass = 1; } if(pass) { /* 取得更高的權限 */ printf("You are root now.\n"); } return 0; }
使用 gcc
編譯:
gcc -o buff buff.c
編譯時會產生一些警告訊息,告知使用者 gets
是比較危險的函數,容易產生緩衝區溢位的漏洞,不過還是可以正常編譯出執行檔。
buff.c: In function ‘main’: buff.c:8:3: warning: ‘gets’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations] gets(buff); ^ /tmp/ccWpQlEe.o: In function `main': buff.c:(.text+0x24): warning: the `gets' function is dangerous and should not be used.
以正常的方式執行:
./buff
Enter the password: gtwang Pass. You are root now.
正常的狀況下,程式會檢查輸入的密碼是否等於 gtwang
,如果密碼正確才會讓只用者取得更高的權限。
由於這個程式存在緩衝區溢位的漏洞,所以如果我們輸入的文字超過緩衝區的範圍時,結果就會不如預期:
./buff
Enter the password: 1234567891234 Fail. You are root now.
當我們輸入的文字長度太長,超過了 buff
的記憶體範圍時,就有可能把後面的記憶體內容覆蓋掉,也就是寫到 pass
這個變數的記憶體位址,意外把原本應該要是 0
的 pass
變數改成不是 0
的值,所以整個程式的邏輯就破壞掉了。
程式的變數在記憶體中的位置是編譯器在安排的,有時候不見得會跟程式碼的順序相同,以下是我加入兩行 printf
觀察變數的記憶體位址的程式碼:
#include <stdio.h> #include <string.h> int main() { int pass = 0; char buff[10]; printf("pass: 0x%08x\n", &pass); printf("buff: 0x%08x\n", buff); printf("Enter the password:\n"); gets(buff); if(strcmp(buff, "gtwang")) { printf("Fail.\n"); } else { printf("Pass.\n"); pass = 1; } if(pass) { /* 取得更高的權限 */ printf("You are root now.\n"); } return 0; }
pass: 0x7ecdbec4 buff: 0x7ecdbeb8 Enter the password: 1234567891234 Fail. You are root now.
這時候若我們將 pass
與 buff
的宣告對調,變成:
char buff[10]; int pass = 0;
則輸出就會不一樣了:
pass: 0x7eaf2eb8 buff: 0x7eaf2ebc Enter the password: 1234567891234 Fail.
所以緩衝區溢位攻擊有時候也是要看運氣,以這個例子來說 pass
的位置要剛好在 buff
之後,才會發生被覆蓋掉的狀況。
這裡只是示範緩衝區溢位的一種狀況,事實上只要是牽涉到記憶體存取的程式碼(例如指標、陣列等),都有可能因為不小心而造成緩衝區溢位,所以在使用 C/C++ 寫程式的時候,記憶體還是要小心管理。