카테고리 없음

Pwntools 기본 사용법

Pwntools 소개

Pwntools 는 리눅스 환경에서 익스플로잇을 짜는 것을 쉽게 할 수 있게 해주는 파이썬 라이브러리다.

 

공식 Github 주소 : https://github.com/Gallopsled/pwntools

공식 문서 주소 : http://docs.pwntools.com/en/stable/

 

 

Pwntools 설치

우분투 환경에서 다음 명령어를 입력하면 Pwntools를 쉽게 설치할 수 있다.

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

 

Pwntools 는 파이썬 2.7 환경만 지원하므로 주의하자.

2020년부로 파이선 2.7 지원이 종료됨에 따라 이제 파이썬 3 를 권장한다.

 

 

기본중의 기본

Pwntools를 사용하려면 파이썬 파일의 처음에 다음과 같이 pwn 모듈을 import 시켜야 한다.

from pwn import *

 

 

다음과 같이 process 클래스의 인스턴스를 만드는 것으로 elf 파일을 실행시킬 수 있다.

r = process("파일이름")

위의 줄을 실행하면 해당 프로그램을 실행시키고, 입력을 대기하게 된다.

이후 실행된 프로그램에는 r 변수를 통해 접근할 수 있다.

 

 

process() 로 실행시킨 프로그램은 입출력을 화면에 직접 할 수 없다. 대신에, r 변수를 통해 함수를 호출시키면서 파이썬 코드를 통해 프로그램의 stdin에 입력을 넣어주고, stdout에서 출력을 받아올 수 있다.

# stdin에 "Hello, World" 문자열을 넣음
r.send("Hello, World")

# stdin에 "Hello, pwntools\n" 문자열을 넣음
r.sendline("Hello, pwntools")


# stdout에서 4바이트의 문자열을 읽어와 반환
r.recv(4)

# stdout에서 한 줄의 문자열을 읽어와 반환
r.recvline()

# stdout에서 "ttt"라는 문자열까지를 반환
r.recvuntil('ttt')


# 유저가 직접 화면에 입출력 할 수 있게 한다.
r.interactive()

r.interactive() 함수가 특이한데, 프로그램을 평소대로 실행했던 때 처럼 입출력 할 수 있게 유저에게 입출력을 다시 돌려준다. 프로그램에 직접 입출력 조작이 필요할 때, 이 함수를 호출하면 된다.

 

 

예제를 통해 자세히 알아보자.

 

#include <stdio.h>
#include <stdlib.h>

int main() {
  int i, a, b, ans;
  srand(time(0));

  for (i = 0; i < 100; i++) {
    a = rand() % 20000;
    b = rand() % 20000;
    printf("#%d: %d + %d = ?\n", i, a, b);
    printf("> ");
    scanf("%d", &ans);
    if (a + b == ans) {
      printf("Correct!\n");
    }
    else {
      printf("Wrong!\n");
      return 0;
    }
  }
  printf("Congrats! flag{welcome_to_pwntools}");
}

쉬운 ctf문제로 나오는 문제 유형 중 하나이다. 100개의 덧셈 문제를 모두 풀면 플래그를 내놓는다.

손으로 하기엔 엄청난 노가다이지만, pwntools 을 활용해 문제를 읽어들이고 답을 입력시키면 빠르게 해결 가능하다.

 

 

from pwn import *

# ./test 를 실행시킨다.
r = process("./test")

# 총 100번 반복한다.
for i in range(100):

    # 몇번째 문제인지 표시
    print("Solving " + str(i))
    
    # 문제의 숫자 앞까지 읽어들이기
    r.recvuntil(":")
    
    # + 앞까지 읽어서 첫번째 항 숫자 읽어들이기
    rawa = r.recvuntil("+")[:-1]
    a = int(rawa)
    
    # = 앞까지 읽어서 두번째 항 숫자 읽어들이기
    rawb = r.recvuntil("=")[:-1]
    b = int(rawb)
    
    # 답을 구함
    ans = a + b

	# 답을 프로그램에 입력시킴
    r.sendline(str(ans))
    
    # Correct! 문자열 읽어줌
    r.recvuntil("Correct!\n")

# 다 끝나면 interactive() 로 제어를 넘겨받음
r.interactive()

[:-1] 은 파이썬의 문법을 활용한 것인데, "ABCDE"[:-1] 의 결과는 "ABCD" 가 된다.

이에 대한 자세한 내용은 "파이썬 문자열 슬라이싱" 을 검색하면 된다.

 

 

위의 c 코드를 gcc test.c -o test 로 컴파일하고 위의 파이썬 코드를 실행시켜 보자.

 

플래그가 나온 것을 확인할 수 있다.

 

 

 

그런데 실제 문제는 로컬의 바이너리로 주지 않고, 원격으로 nc로 접속해야 할 때가 많다.

이에 따라 pwntools 에서도 원격으로 접속할 수 있는 기능을 제공한다.

 

r = remote("주소", 포트)

process 대신에 remote 클래스를 사용하면 원격에 접속할 수 있다.

remote 를 사용해도 process 로 실행했을 때와 완전히 동일하게 send 와 recv 계열 함수를 사용할 수 있다.

 

nc 123.456.789.123 12345

로 접속하는 것은 다음과 동일하다.

r = remote("123.456.789.123", 12345)