pwntools

현재·2023년 12월 26일

sys

목록 보기
7/9

pwntools API 사용법

1. process & remote

process 함수는 익스플로잇을 로컬 바이너리를 대상으로 할 때 사용하는 함수 이다. 보통 잇스플로잇을 테스트하고 디버깅하기 위해 사용된다.

remote 함수는 원격 서버를 대상으로 할 때 사용하는 함수입니다. 대상 서버를 실제로 공격하기 위해 사용합니다.

from pwn import *
p = process('./test')  # 로컬 바이너리 'test'를 대상으로 익스플로잇 수행
p = remote('example.com', 31337)  # 'example.com'의 31337 포트에서 실행 중인 프로세스를 대상으로 익스플로잇 수행
 

2. send

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'을 입력
 

3. recv

recv는 프로세스에서 데이터를 받기 위해 사용한다.

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에 저장
 

recv()와 recvn()의 차이점

recv(n)은 최대 n 바이트를 받는 것이므로, 그만큼을 받지 못해도 에러를 발생시키지 않지만, recvn(n)의 경우 정확히 n 바이트의 데이터를 받지 못하면 계속 기다린다.

4. packing & unpacking

pwntools는 익스플로잇 작성 시 리틀 엔디언의 바이트 배열로 값 변환하거나, 그 반대로 변환하는데 유용한 함수들을 제공한다.

#!/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

5. interactive

pwntools의 함수를 사용하면 셸이나 익스플로잇 상황에서 터미널 입력과 출력을 간단히 처리할 수 있습니다.

from pwn import *
p = process('./test')
p.interactive()

6. ELF

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에 저장

7. context.log

pwntools에는 디버그의 편의를 돕는 로깅 기능이 있어 익스플로잇에 버그가 발생할때 익스폴로잇을 디버깅 할 수 있다.

from pwn import *
context.log_level = 'error' # 에러만 출력
context.log_level = 'debug' # 대상 프로세스와 익스플로잇간에 오가는 모든 데이터를 화면에 출력
context.log_level = 'info'  # 비교적 중요한 정보들만 출력
 

8. context.arch

pwntools는 아키텍처 정보를 지정하여 셸코드 생성 및 어셈블링과 같은 기능을 제공하며, 이로써 몇몇 함수들의 동작을 조절할 수 있습니다.

from pwn import *
context.arch = "amd64" # x86-64 아키텍처
context.arch = "i386"  # x86 아키텍처
context.arch = "arm"   # arm 아키텍처

9. shellcraft

pwntools는 자주 사용되는 셸 코드를 손쉽게 활용할 수 있게 해주지만, 정적 특성과 프로그램 제약으로 인해 해당 셸 코드가 모든 상황에 적합하지는 않습니다.
특히, 실행 시점의 메모리 상태를 반영하지 못하며, 프로그램의 제약에 따라 길이나 사용 가능한 문자가 제한될 수 있습니다.
따라서 특정한 제약이 있는 상황에서는 직접 셸 코드를 작성하는 것이 더 적절할 수 있습니다.

#!/usr/bin/env python3
# Name: shellcraft.py

from pwn import *
context.arch = 'amd64' # 대상 아키텍처 x86-64

code = shellcraft.sh() # 셸을 실행하는 셸 코드 
print(code)
 
$ python3 shellcraft.py
    /* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push b'/bin///sh\x00' */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    ...
    syscall

10. asm

pwntools는 어셈블 기능을 제공합니다. 이 기능도 대상 아키텍처가 중요하므로, 아키텍처를 미리 지정해야 합니다.

#!/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'
 
profile
개발자

0개의 댓글