소학회/워게임 추가 공부

[pwnable]Dreamhack_baby-bof : pwntools 추가 공부

haerim9.9 2025. 9. 30. 22:18

워게임을 풀고 공부+실습을 진행했다.

https://hae9-9.tistory.com/82


참고 자료 1

참고 자료 2

 

pwntools: 파이썬 기반 익스플로잇 자동화 도구, 어셈블/디스어셈블/패킹/언패킹 등의 여러 작업 수행 가능

*익스플로잇(exploit): 보안 취약점을 이용하여 공격하도록 설계된 명령, 스크립트, 프로그램

 

 

1. pwntools 설치

ubuntu 20.04 환경에서 아래 명령어를 입력해 준다.

$ apt-get update
$ sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
$ python3 -m pip install --upgrade pip
$ python3 -m pip install --upgrade pwntools

 

설치 확인 명령어

$ python3 -c 'from pwn import *'

 

 

실행

python3 - << 'PY' #파이썬 실행
from pwn import * #임포트

 

 

2. 패킹/언패킹: struct 모듈 사용 -> pwntools를 사용하면 간편해짐

p32(x): 32비트 정수 x를 리틀엔디언 바이트 문자열로 패킹

u32(b): 바이트열 b를 정수로 언패킹

*패킹(packing): 여러 개의 객체를 하나로 묶는 것 <-> 언패킹(unpacking): 다시 푸는 것

u32로 패킹, 그리고 u32로 패킹한 것을 hex값으로 출력, u32로 패킹했던 것을 다시 언패킹한 결과를 볼 수 있다.

그외 32비트 외에도 8비트, 16비트, 64비트.. 등 가능하다.

 

 

3. 조립 및 분해

조립(asm): 어셈블리어 -> 바이트 변환

분해(disasm): 바이트 -> 어셈블리어 변환

asm 명령어로 mov eax, 0을 b8000000000로 변환

disasm 명령어로 바이트를 사진과 같은 어셈블리어로 변환해 준다.

 

*unhex: 16진수 문자열을 바이트로 변환

*enhex: 바이트 값을 16진수로 보여줌(예시: enhex(b'\xb8\x00\x00\x00\x00') -> b'b800000000')

 

 

4. 대상 아키텍처 및 OS 설정

아키텍처: i386(x86, 32-bit), amd64(x86_64, 64-bit) 등

OS: Linux(ELF) vs Windows(PE)

-> 익스플로잇 방식 달라짐. 바이너리의 아키텍처/OS를 확인해서 공격 스크립트를 작성해야 함.

같은 nop이어도 아키텍처가 arm인 경우 사진과 같이 달라지는 것을 확인할 수 있음.

 

*해당 명령어에서 오류날 경우

더보기

arm 아키텍처용 패키지가 없어서 오류남. 아래 코드 복붙할 것. 

sudo apt update

# 일반적인 binutils (기본 x86 어셈블리용)
sudo apt install -y binutils

# ARM 32-bit / 64-bit용 cross binutils (일반적으로 이걸 설치하면 pwntools에서 asm('...', arch='arm') 같은게 동작함)
sudo apt install -y binutils-arm-linux-gnueabi binutils-arm-linux-gnueabihf

# 또는 범용(여러 타깃의 binutils 모음)
sudo apt install -y binutils-multiarch

# 추가로 cross gcc가 필요할 경우 (옵션)
sudo apt install -y gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf

 

context를 이용해서 전역 변수에 아키텍처, OS, word size, endian 설정 가능

context.arch      = 'i386'
context.os        = 'linux'
context.endian    = 'little'
context.word_size = 32

 

context의 단축형도 존재한다.

 

 

5. 로깅/디버그 출력

log_level로 레벨 변경 가능.

레벨 종류:

  • debug: 가장 상세함. 문제 분석/디버깅할 때 유용
  • info: 일반 정보 수준. 보통 개발 단계, 자동화 스크립트에서 기본으로 사용
  • warning/warn: 비정상적이지만 치명적이지 않은 상황을 알림
  • error: 오류 상황 출력
  • critical: 치명적 에러

-> pwntools 내부의 자동 로그가 연결될 때 무슨 바이트를 보냈는지, 받았는지를 얼마나 보여줄지 결정함.

context.log_level = 'debug'

 

 

6. ELF 조작

ELF(Executable and Linkable Format): 리눅스에서 실행 파일/공유라이브러리/오브젝트 파일을 담는 표준 포맷. 프로그램 구조를 담고 있어 익스플로잇에 중요한 정보 출처가 됨.

 

e = ELF('/bin/cat')
print(hex(e.address)) 
0x400000
print(hex(e.symbols['write'])) 
0x401680
print(hex(e.got['write'])) 
0x60b070
print(hex(e.plt['write'])) 
0x401680

ELF('/bin/cat'): 괄호 안의 ELF 파일을 읽어서 파일 내부의 정보를 파싱. 파이썬 객체 형태로 돌려줌.

 

ELF 속성

e.arch: 아키텍처

e.bits: 32 or 64

e.symbols: 심볼 이름 -> 주소(파일 기준)

e.got: GOT 엔트리 주소 dict

e.plt: PLT 엔트리 주소 dict

e.search(b'/bin/sh'): 바이너리 내에서 바이트/문자열 검색

e.sections / e.segments: 섹션/세그먼트 정보

 

 

7. 연결 만들기: 챌린지 바이너리와 통신하기 위해 pwnlib.tubes 모듈 사용

  • remote(host, port): TCP 연결(소켓)을 만들어주는 함수
  • send(b'data'):
  • recv(n): n바이트를 읽음
  • recvuntil(delim, drop=False): delim을 만날 때까지 읽고, delim을 포함한 바이트 반환. (true일 경우, delim을 제거해서 반환)
  • recvline(): 한 줄을 읽음
  • recvall(timeout=...): 가능한 모든 데이터를 읽어 반환
  • interactive(): pwntools이 터미널과 원격으로 연결해서 수동 조작할 수 있게 함
  • close(): 연결 닫기
conn = remote('ftp.ubuntu.com',21)
conn.recvline() 
b'220 ...'
conn.send(b'USER anonymous\r\n')
conn.recvuntil(b' ', drop=True)
b'331'
conn.recvline()
b'Please specify the password.\r\n'
conn.close()