

3주가 사라졌다
제일 처음에 시도했던 10레벨 문제다. 중간에 안풀려서 Showdown(10)이나 redis_made(10)을 풀어보긴 했는데,
다 실패하고 결국 가장 많은 진척도를 보였던 Reconquista에 매달렸다.
결국 핵심 아이디어 포인트는 문제에 있었다. std::thread() 를 어떻게 활용할 것인가.
심지어, C++ 메뉴 기반 pwnable이라 루프가 스레드 안에서 동작해서 더 헤맸던 감이 있다.
분석 시작하겠다.
TLS에 std::string* 포인터 배열 200개가 배치되고,
경계 검사 없이 인덱스 증가시 OOB 쓰기로 뒤따르는 TCB에 침범한다.
이 문제는 TLS 바로 뒤에 TCB(tcbhead_t)가 연속으로 배치되는 구성이다.
tcb 헤드 앞부분의 중요 구조체를 좀 보면,
struct tcbhead_t {
void *tcb; // +0x00
dtv_t *dtv; // +0x08 ← 목표
void *self; // +0x10
int multiple_threads; // +0x18
uintptr_t sysinfo; // +0x20
uintptr_t stack_guard; // +0x28 ← 스택 카나리
...
}
이 부분이 우리가 오버플로우로 덮고 싶은 구간이다.
문제는 0x08에서부터 덮기 시작하면 6번째 포인터(0x28)을 건드리는 시점에 stack_guard(canary)가 망가진다는 것이다.
즉, 덮을 수 있는 포인터는 최대 5개로 이 제약으로 인해 dtv 포인터 겨냥이 최선이라고 확신을 가졌다.
glibc 동작 경로는 다음과 같은데,