[DreamHack] pwntools
설치
$ apt-get update
$ 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
pwntools API 사용법
https://docs.pwntools.com/en/latest/
pwntools — pwntools 4.13.0dev documentation
© Copyright 2016, Gallopsled et al.. Revision bf7abc05.
docs.pwntools.com
process
익스플로잇을 로컬 바이너리를 대상으로 할 때 사용하는 함수
익스플로잇을 테스트하고 디버깅
remote
원격 서버를 대상으로 할 때 사용하는 함수
대상 서버를 실제로 공격하기 위해 사용
from pwn import *
p = process('./test') # 로컬 바이너리 'test'를 대상으로 익스플로잇 수행
p = remote('example.com', 31337) # 'example.com'의 31337 포트에서 실행 중인 프로세스를 대상으로 익스플로잇 수행
send
데이터를 프로세스에 전송하기 위해 사용
from pwn import *
p = process('./test')
p.send(b'A') # ./test에 b'A'를 입력
p.sendline(b'A') # ./test에 b'A' + b'\n'을 입력
p.sendafter(b'hello', b'A') # ./test가 b'hello'를 출력하면, b'A'를 입력
p.sendlineafter(b'hello', b'A') # ./test가 b'hello'를 출력하면, b'A' + b'\n'을 입력
recv
프로세스에서 데이터를 받기 위해 사용
recv(n)
최대 n 바이트를 받는 것이므로, 그만큼을 받지 못해도 에러를 발생시키지 않음
recvn(n)
정확히 n 바이트의 데이터를 받지 못하면 계속 기다림
from pwn import *
p = process('./test')
data = p.recv(1024) # p가 출력하는 데이터를 최대 1024바이트까지 받아서 data에 저장
data = p.recvline() # p가 출력하는 데이터를 개행문자를 만날 때까지 받아서 data에 저장
data = p.recvn(5) # p가 출력하는 데이터를 5바이트만 받아서 data에 저장
data = p.recvuntil(b'hello') # p가 b'hello'를 출력할 때까지 데이터를 수신하여 data에 저장
data = p.recvall() # p가 출력하는 데이터를 프로세스가 종료될 때까지 받아서 data에 저장
packing & unpacking
어떤 값을 리틀 엔디언의 바이트 배열로 변경하거나, 또는 역의 과정으로 변경
#!/usr/bin/env python3
# Name: pup.py
from pwn import *
s32 = 0x41424344
s64 = 0x4142434445464748
print(p32(s32))
print(p64(s64))
s32 = b"ABCD"
s64 = b"ABCDEFGH"
print(hex(u32(s32)))
print(hex(u64(s64)))
결과
$ python3 pup.py
b'DCBA'
b'HGFEDCBA'
0x44434241
0x4847464544434241
interactive
셸을 획득했거나, 익스플로잇의 특정 상황에 직접 입력을 주면서 출력을 확인하고 싶을 때 사용
호출하고 나면 터미널로 프로세스에 데이터를 입력하고, 프로세스의 출력을 확인할 수 있음
from pwn import *
p = process('./test')
p.interactive()
ELF
from pwn import *
e = ELF('./test')
puts_plt = e.plt['puts'] # ./test에서 puts()의 PLT주소를 찾아서 puts_plt에 저장
read_got = e.got['read'] # ./test에서 read()의 GOT주소를 찾아서 read_got에 저장
context.log
익스플로잇에 버그가 발생하면 익스플로잇도 디버깅해야 함
로그 레벨은 context.log_level 변수로 조절 가능
from pwn import *
context.log_level = 'error' # 에러만 출력
context.log_level = 'debug' # 대상 프로세스와 익스플로잇간에 오가는 모든 데이터를 화면에 출력
context.log_level = 'info' # 비교적 중요한 정보들만 출력
context.arch
아키텍처 정보를 프로그래머가 지정
from pwn import *
context.arch = "amd64" # x86-64 아키텍처
context.arch = "i386" # x86 아키텍처
context.arch = "arm" # arm 아키텍처
shellcraft
x86-64를 대상으로 생성할 수 있는 여러 종류의 셸 코드를 찾아볼 수 있음
https://docs.pwntools.com/en/stable/shellcraft/amd64.html
pwnlib.shellcraft.amd64 — Shellcode for AMD64 — pwntools 4.11.1 documentation
key (int,str) – XOR key either as a 8-byte integer, If a string, length must be a power of two, and not longer than 8 bytes. Alternately, may be a register.
docs.pwntools.com
asm
어셈블 기능을 제공
대상 아키텍처가 중요하므로, 어셈블 기능을 제공
#!/usr/bin/env python3
# Name: asm.py
from pwn import *
context.arch = 'amd64' # 익스플로잇 대상 아키텍처 'x86-64'
code = shellcraft.sh() # 셸을 실행하는 셸 코드
code = asm(code) # 셸 코드를 기계어로 어셈블
print(code)
결과
$ python3 asm.py
b'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05'