[Pwn] Pwnable · 작성: 2026-07-19 19:28:48 · 조회 2
스택에 선언된 버퍼(배열)에, 그 크기보다 긴 데이터를 길이 검사 없이 써넣을 수 있을 때 발생하는 가장 기본적인 메모리 손상 취약점이다. Pwnable을 처음 배울 때 제일 먼저 만나는 주제이기도 하다.
void vuln() {
char buf[64];
gets(buf); // 길이 검사가 없다 — 64바이트를 넘겨도 그냥 계속 쓴다
}
gets, strcpy, sprintf처럼 목적지 버퍼의 크기를 모른 채 복사하는 함수들이 전형적인 취약점 원인이다. buf가 64바이트인데 100바이트를 입력하면, 나머지 36바이트가 buf 뒤에 있는 saved RBP와 리턴 주소를 덮어쓴다.
pwntools의 cyclic 패턴(aaaabaaacaaadaaa... 식으로 4바이트마다 겹치지 않는 패턴)을 입력하고, 크래시가 난 시점의 RIP 값을 cyclic_find에 넣으면 오프셋이 나온다.A를 채운 뒤 그 다음 8바이트를 컨트롤 가능한 값(예: 0x4141414141414141)으로 채워서, 실제로 RIP가 그 값으로 바뀌는지 확인한다.from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
payload = cyclic(200)
p.sendline(payload)
p.wait()
core = p.corefile
offset = cyclic_find(core.read(core.rsp, 4))
print("offset:", offset)
2000년대에는 이 정도로 셸코드 직접 실행까지 끝났지만, 지금은 컴파일러/OS가 기본으로 여러 보호 기법을 켜둔다.
checksec으로 이 보호 기법들이 뭐가 걸려있는지 먼저 확인하고, 각각을 어떻게 우회하는지는 이어지는 글에서 하나씩 다룬다.
$ checksec ./vuln
Canary : Disabled
NX : Enabled
PIE : Disabled
RELRO : Partial
이 예시라면 카나리는 없고 NX만 걸려있으니, 셸코드를 직접 스택에 심는 대신 ROP로 우회해야 한다는 뜻이다.