實驗概述
本實驗的學習目標是讓學生獲得緩沖區溢出攻擊的一種有趣變體——return-to-libc攻擊實驗的親身體驗。這種攻擊可以繞過目前在主要linux操作系統中實現的現有保護方案。利用緩沖區溢出漏洞通常的方法是使緩沖區溢出,然后在溢出部分的返回地址指向一段惡意的shellcode,使程序跳轉到存儲在堆棧中的shellcode。為了防止這些類型的攻擊,一些操作系統允許管理員關閉堆棧的可執行功能。因此跳轉到shellcode將導致程序失敗。不幸的是,上述保護機制不是足夠安全的。存在一種成為return-to-libc攻擊的緩沖區溢出攻擊的變體,其不需要可執行堆棧,甚至不需要shellcode。相反,它會導致受到攻擊的程序跳轉到一些現有的代碼。例如已經加載到內存中的libc庫中的system()函數。在這個實驗中,學生被給予一個包含緩沖區溢出漏洞的程序,他們的任務是利用該漏洞,開發一個返回到libc庫函數的攻擊,最終獲得root權限。除了這些攻擊之外,學生們還將參與學習在ubuntu中實施的防止緩沖區溢出攻擊的幾項保護措施。學生需要評估方案是否有效,並解釋評估過程。
第二部分:實驗任務
該實驗共有三個任務:
- 漏洞利用
- 地址隨機化
- StackGuard保護機制
任務一:漏洞利用
1.編譯代碼是關閉地址隨機化機制和非執行棧機制
sudo sysctl -w kernel.randomize_va_space=0 //注意:這里等號和0之間不能有空格
在進行代碼編譯時,加上-fno-stack-protector,即可關閉ubuntu上StackGuard保護機制,加上-z -execstack/noexecstack即可打開或關閉可執行棧的機制。
在本實驗中,我們按照如下命令編譯含有緩沖區溢出漏洞的代碼段:
su root
gcc -fno-stack-protector -z noexecstack -o retlib retlib.c
chmod 4755 retlib / chmod u+s retlib
exit
2.獲取/bin/sh地址
為了獲取/bin/sh的內存地址,需要編寫一個程序。
//getenvaddr.c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc,char const *argv[]) { char *ptr; if(argc<3) { printf("Usage: %s <environment var> <target program name>\n", argv[10]); exit(0); } ptr = getenv(argv[1]); ptr += (strlen(argv[0]) - strlen(argv[2])) * 2 ; printf("%s will be at %p \n", argv[1], ptr ) ; return 0;
}
其中,getenv的參數為一個環境變量,因此我們需要創建一個環境變量來記錄路徑export BIN_SH="/bin/sh"。調用程序./getenvaddr BIN_SH ./retlib得到/bin/sh的地址。
如上面截圖所示,/bin/sh的地址為:0xbffffe39。
3. 獲取system()和exit()地址
這兩個程序是駐留在內核態的,所有程序共享內核態的函數,因此我們可以用gdb來獲取這兩個程序的地址。
如上圖所示,在main函數設置斷點,然后運行,運用gdb調試命令,我們可以得到system()函數的地址是0xb7e5f430,exit()函數的地址是0xb7e52fb0。
將上述獲得的三個地址寫到代碼中,
第三部分:
實驗指導:了解函數調用機制
3.1 獲取libc庫函數的地址
用gdb命令進行獲取。
3.2 將shell字符串放在內存中
本實驗的一個挑戰是將字符串“/bin/sh”放入內存中,並獲取其地址。這可以使用環境變量來實現。執行C程序時,它會繼承執行它的shell的所有環境變量。環境變量SHELL直接指向/bin/bash並且環境變量同時也被其他程序需要。所以我們引入一個新的shell變量MYSHELL,並指向zsh。
$ export MYSHELL = /bin/sh
我們將使用該變量的地址作為system()函數調用的參數。這個變量的位置在內存中使用以下代碼就可以發現:
void main() { char 8shell = getenv("MYSHELL"); if(shell) prinf("%x\n",(unsigned int)shell); }
如果地址隨機化被關閉,將會發現打印出的地址是相同的。然而,當您運行漏洞程序retlib時,環境變量的地址可能與通過運行上述程序獲得的地址完全相同。當您更改程序名稱時,這樣的地址甚至會更改(文件名中的字符數不一樣)。好消息是,shell的地址將與上述程序打印出來的地址相當接近。因此,您可能喲啊嘗試幾次才能成功。
3.3 理解棧
要知道如何進行return-to-libc攻擊,必須了解堆棧的工作原理。我們使用一個小程序來了解函數調用對堆棧的影響。
/* foobar.c */
#include <stdio.h>
void foo(int x)
{
printf("Hello world: %d\n", x);
}
int main()
{
foo(1);
return(0);
}
我們可以用“gcc -S foobar.c”來將程序編譯成匯編代碼。