R136A1

[SWING] python pwntools 사용법 + 응용 예제 본문

SWING/[21-1] SYSTEM

[SWING] python pwntools 사용법 + 응용 예제

r136a1x27 2021. 4. 28. 03:48

노션으로 이전 완료

공식깃허브 공식문서

pwntools는 python 2 에서 쓰는 것이 호환 더 잘 됨

python 3는 더 디테일해서 간단한 문제 풀 때는 2가 나음

 

github.com/Gallopsled/pwntools-tutorial/blob/master/bytes.md

Python2 str == bytes

exploit을 작성할 때 매우 편리하다.

encode(=b' '), decode 작업 없어도 됨

 

Python3 unicode == str

byte로 변환하는 과정 필요

→ 아래에서 str을 인자로 쓸 경우 b'____' 안에 넣어야 한다

대부분의 반환값도 byte이기때문에 출력하려면 .decode() 해야 한다

 

 

from pwn import *

1. 연결 객체 생성

r = remote('주소', 포트번호) # 외부 서버에 있는 문제에 접속

p = process('주소') # 로컬 문제 파일 실행키셔 프로세스 형태로 로드

 

2. 데이터 받아오기(=위치포인터 이동하기)

p.recv(크기) # int만큼 데이터를 받아옴

p.recvn(buffer) # buffer만큼의 데이터가 차야 받아옴

p.recvline() # 현재 위치포인터 줄 끝(\n)까지 받아온 뒤 다음 줄로 이동

p.recvuntil(bytes) # 현재 위치포인터부터 해당 바이트(b'문자열')까지 받아옴(현재 줄 넘어서는 X)

 

3. 데이터 보내기

p.send(bytes) # 데이터 보냄 read()

p.sendline(bytes) # 데이터 보낼 때 \n 붙여서 보냄 scanf() gets() fgets()

                         string의 경우 자동으로 encode() 하나, int는 str로 변환 필요

p. sendafter(str, send_str)

p. sendlineafter(str, send_str) # str를 받으면 그 때 send_str\n 보냄

 

4. interactive() 쉘과 직접적으로 명령을 전송하고 수신할 수 있는 함수

수동 대화 상태로 돌아가는 것. 코드 맨 마지막에 사용한다

 

5. 연결 종료

p.close()

 

 

다른 연결 객체

s = ssh("username", "localhost", port=포트번호, password="비밀번호")

s['whoami'] # 쉘에서 명령어 실행

s.set_working_directory()

s.ls()

s.download_file('주소') # 파일 다운로드

sh = s.run('sh') # 

sh.sendline(b'명령어') 

sh.recvline( )

s.close()


packing

문자열의 경우 ASCII코드 10진수 값이 그대로 들어감 \x67 = D

남은 바이트는 00으로 채워진다.

p32(____, endian='big') # 32비트(4byte) default 리틀 엔디안 방식으로 패킹

p64(____, endian='big') # 64비트(8byte) default 리틀 엔디안 방식으로 패킹

 

unpacking

u32(____, endian='big')u64(____, endian='big') 

 

 

elf = ELF("파일주소")

# 바이너리를 인식시켜 적용되어 있는 보호 기법과 PLT, GOT 와 같은 ELF 정보를 가져오는 함수

elf.plt['함수이름'] # 함수의 PLT 주소를 찾음

elf.got['함수이름'] # 함수의 GOT 주소를 찾음

elf.symbols 등으로 접근 가능

 

 

+gdb와 연동

gdb.attach(연결객체) # 현재 프로그램의 상태에서 gdb 실행. local 파일 로드했을 때만 가능

 

+로그 보기

context.log_level = "info"

context.log_level = "debug"


먼저, 문제가 c 파일로 주어졌기 때문에 실행 파일로 변환하는 과정이 필요하다

gcc -o 출력파일이름 컴파일할파일이름

-o 옵션: 인자로 준 파일 이름으로 실행 파일을 출력한다

gcc -o swing_pwn_chall swing_pwn_chall.c 명령어를 통해 컴파일한다

(gcc는 설치해야 한다)

 

문제를 실행시켜보면 아래와 같은 형태로 실행된다

#문제번호: number1 + number2 = ?\n

> 입력하기

 

 

코드 작성

idle 내에서나 ./answer.py로 실행할 경우 pwn module을 찾지 못하는 관계로

(idle은 왜인지 모르겠는데, ./answer.py로 실행할 경우 (default)python2로 실행되는 듯 하다)

terminal에서 python3 answer.py로 실행해준다

 

코드 작성하면서 뭐가 안될때는 로그를 출력해본다

context.log_level = "debug"

 

process를 통해 연결객체를 만들어주고

 

for i in range(20): 을 통해 20번 실행하도록 한다(.c 파일코드에서 20까지 실행되도록 했기 때문)

 

p.recvuntil(b': ') : 까지 받아온다

#문제번호number1 + number2 = ?\n

                   ▲   

입력하기

 

p.recvuntil(b' = ') = 까지 받아온다

#문제번호number1 + number2 = ?\n

                   ▲                                         

입력하기

 

string에는 현재 number1 + number2 = 가 들어있다

 + 를 기준으로 split 하면 [0]에 number1 [1]에 number2 = 가 있으므로

[1]은 split을 한번 더 사용해  =을 떼준다

 

p.recvline() \n 까지 받아온다

#문제번호number1 + number2 = ?\n

                   ▲                                          ▲   

입력하기

 

> 로 이동시키지 않아도 됨. 바로 scanf()가 실행되어 입력모드로 진입하기 때문


다 풀고 다른 분들 풀이(+swing ctf write up)를 본 결과 슬라이싱, 연산에 있어서 더 효율적인 방법 존재

1) p.recvuntil(':')까지 이동 후

number1 = p.recvuntil('+')[:-1] 

number2 = p.recvuntil('=')[:-1]

 

p.sendline(str(number1+number2))

 

2) p.recvuntil(':')까지 이동 후

expr = p.recvuntil('=')[:-1]

 

p.sendline(str(eval(expr))

이게 최선의 풀이. 이렇게 풀면 연산자 제약도 없이 연산 가능

 

Comments