본문 바로가기

DreamHack: System Hacking/F Stage 3

Tool: pwntools (pwntools 사용법 및 명령어 정리)

pwntools

0. import
python에서 "from pwn import *"를 첫 줄에 적어준다

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
process, remote == 익스플로잇 함수
process == "로컬 바이너리" 대상 익스플로잇 함수(보통 테스트&디버깅을 위해 사용)
remote == "원격 서버" 대상 익스플로잇 함수(대상 서버를 실제로 공격하기 위해 사용)

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

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
send == 데이터를 프로세스에 전송하기 위해 사용. pwntools에는 send에 관한 다양한 함수 존재

ex)
from pwn import *
p = process('./test')
p.send('A')  # ./test에 'A'를 입력
p.sendline('A') # ./test에 'A'+'\n'을 입력
p.sendafter('hello', 'A')  # ./test가 'hello'를 출력하면, 'A'를 입력
p.sendlineafter('hello', 'A')  # ./test가 'hello'를 출력하면, 'A' + '\n'을 입력

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
recv == 프로세스에서 데이터를 받기 위해 사용. pwntools에는 recv에 관한 다양한 함수 존재
recv(n) == 최대 n바이트까지 받음(-> n바이트 이하의 데이터를 입력받아도 에러(X))
recvn(n) == 정확히 n바이트의 데이터만을 받음(-> n바이트보다 작은 데이터가 들어오면 n바이트가 충족될 때까지 입력 대기함)

ex)
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('hello')  # p가 출력하는 데이터를 'hello'가 출력될 때까지 받아서 data에 저장
data = p.recvall()  # p가 출력하는 데이터를 프로세스가 종료될 때까지 받아서 data에 저장

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
packing&unpacking

packing(p함수) -> 바이트 배열을 정수값으로 변환
unpacking(u함수) -> 정수값을 바이트 배열로 변환

코드: 
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)))

결과:
b'DCBA'
b'HGFEDCBA'
0x44434241
0x4847464544434241

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
interactive == '쉘 획득' or '익스플로잇의 특정 상황'에서 직접 입력을 주면서 출력을 확인할 수 있다
호출 후, 터미널로 프로세스에 데이터를 입력하고, 프로세스의 출력을 확인할 수 있다.

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

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
ELF == ELF 헤더를 참조함으로서, 익스플로잇에 사용될 수 있는 각종 정보를 확인할 수 있다.

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

*plt와 got는 아직 안 배웠음

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
context.log == 익스플로잇을 디버깅 하는 걸 도와줌

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

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
context.arch == pwntools는 쉘코드를 생성, 코드를 어셈블&디스어셈블 하는 기능 등을 갖고 있다
이는 아키텍처의 영향을 받으므로 'context.arch'는 아키텍처를 지정할 수 있게 한다

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

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
shellcraft == 쉘 코드 작성 시 제약 조건들이 있을 때, 직접 쉘 코드를 작성할 수 있게 해준다

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

code = shellcraft.sh() # 셸을 실행하는 셸 코드 
print(code)

어셈블리 결과:
push 0x68
    mov rax, 0x732f2f2f6e69622f
    ...
    syscall

-----------------------------------------------------------------------------------------------------------------------------------------------------------------
asm == 어셈블 기능을 제공

ex) 
from pwn import *
context.arch = 'amd64' # 익스플로잇 대상 아키텍처 'x86-64'

code = shellcraft.sh() # 셸을 실행하는 셸 코드
code = asm(code)       # 셸 코드를 기계어로 어셈블
print(code)

#################################### 총 정리 ####################################

process & remote: 로컬 프로세스 또는 원격 서버의 서비스를 대상으로 익스플로잇 수행
send & recv: 데이터 송수신
packing & unpacking: 정수를 바이트 배열로, 또는 바이트 배열을 정수로 변환
interactive: 프로세스 또는 서버와 터미널로 직접 통신
context.arch: 익스플로잇 대상의 아키텍처
context.log_level: 익스플로잇 과정에서 출력할 정보의 중요도
ELF: ELF헤더의 여러 중요 정보 수집
shellcraft: 다양한 셸 코드를 제공
asm: 어셈블리 코드를 기계어로 어셈블

 

 

느낀 점: pwntools를 잘 사용하기 위해서는 exploit과 shellcode에 대한 더 많은 공부가 필요할 것 같다.

다음 stage들을 거쳐나가며 exploit과 shellcode에 대한 이해도를 높이고, 스스로도 많은 공부를 해야할 것 같다.

'DreamHack: System Hacking > F Stage 3' 카테고리의 다른 글

Tool: gdb (gdb 명령어 정리)  (0) 2023.03.31