2026 SPACE WAR - 02.TAURUS CTF 문제 풀이
0. 개요
문제 이름 : draft
문제 유형 : Pwnable
이 문제는 일반적인 heap 취약점이 아니라,
스택 lifetime 관리 실패로 인해 발생한 dangling pointer를 이용하는 문제다.
핵심은 매우 단순하다.
스택에 있던 데이터를 전역 포인터로 들고 있다가, 함수가 끝난 뒤에도 계속 사용하는 구조
이를 이용해:
- 스택 메모리 leak
- 스택 return address overwrite
- libc ROP 실행
까지 이어지는 exploit chain을 구성할 수 있다.
1. 프로그램 구조
메뉴 구성:
- compose
- peek
- amend
- discard
- quit
처음 보기에는 단순한 메모 프로그램처럼 보이지만,
내부 구현이 문제의 핵심이다.
2. 핵심 취약점
2-1. compose (문제의 시작)
1
2
3
4
5
6
7
8
void compose() {
char buf[0x80];
read_line(buf, 0x80);
global_draft_ptr = buf;
global_draft_len = strlen(buf);
}
buf는 스택 변수 그런데 이 주소를 전역 변수에 저장
결과
함수 종료 → buf 소멸 → global_draft_ptr는 dangling pointer
죽은 스택 프레임을 계속 참조
2-2. peek (스택 leak)
- *(global_draft_ptr + off + i)
문제점:
- off 검증 없음
- 이미 invalid pointer 사용
결과:
- 임의 offset으로 스택 메모리 읽기 가능
- stack leak 가능
2-3. amend (스택 overwrite)
global_draft_ptr[global_draft_len + i] = ch;
문제점:
- 여전히 dangling pointer 사용
- write 위치 제한 없음
결과:
스택 위쪽 arbitrary write
return address overwrite 가능
3. 취약점 정리
이 문제는 두 가지가 결합된다.
- dangling stack pointer 생성
- read/write primitive 제공
결과:
- peek → stack leak
- amend → stack overwrite
4. 스택 leak
peek를 이용하면 다음 값들이 노출된다.
offset 0x88 → canary
offset 0x90 → saved rbp
offset 0x98 → PIE return
offset 0xd8 → libc return
계산:
libc_base = leaked_ret - 0x2a1ca
pie_base = leaked_ret - 0x178e
ROP 준비 완료
5. 공격 전략
5-1. 왜 amend의 return을 덮는가
일반적인 방법:
main return overwrite
하지만 더 쉬운 방법:
amend 함수의 return 바로 덮기
스택 구조:
[canary]
[saved rbp]
[saved rip]
여기만 정확히 덮으면 바로 ROP
5-2. 공격 흐름
- compose → dangling pointer 생성
- peek → canary + libc leak
- amend → 자신의 return overwrite
- 함수 종료 → ROP 실행
6. ROP 구성
사용 gadget (libc):
ret
pop rdi ; ret
system
exit
실행 명령:
cat /home/ctf/flag
ROP 체인:
ret
pop rdi
&”cat /home/ctf/flag”
system
exit
ret는 stack alignment 용
7. 익스플로잇 흐름
전체 흐름:
- compose (빈 문자열)
- peek → stack leak
- libc base 계산
- amend → payload writereturn → ROP 실행
- flag 출력
8. 전체 공격 흐름 정리
- 스택 dangling pointer 생성
- peek로 stack leak
3 canary + libc 주소 획득 - amend로 stack overwrite
- return address 조작
- ROP 실행
- flag 획득
9. 포인트
이 문제의 포인트는 다음이다.
- heap이 아니라 stack lifetime bug
- local buffer를 global pointer로 저장
- 함수 종료 후에도 계속 사용
- read + write primitive 제공
매우 전형적인 실수지만 강력한 취약점
10. 정리
스택 버퍼의 lifetime을 잘못 관리하여 발생한 dangling pointer를 이용해 stack leak과 return overwrite를 수행하고 libc ROP로 flag를 획득하는 문제
11. 최종 플래그
1
hspace{my_dr4ft_0utl1v3d_1ts_0wn_st4ck_fr4me}