pwntools

Srevil·2022년 7월 13일
0

Dreamhack

목록 보기
7/7
post-thumbnail

서론

Python 과 파이프( | )를 이용하여 간단한 스택 오버플로우 익스플로잇
Python 으로 페이로드 생성
Pipe 통해 프로그램에 전달

익스플로잇이 조금만 복잡해져도 아래와 같은 이유로 위와 같은 방식 이용 어려움

  • 페이로드 생성하기 위해 복잡한 연산 필요
  • 프로세스와 반복적으로 데이터 주고 받아야 함

그래서 펄, Python, C 등으로 익스플로잇 스크립트 또는 바이너리 제작하여 사용

Figure 2

socket 모듈을 사용한 초기 파이썬 익스플로잇 스크립트의 예

Python 으로 여러 개의 익스플로잇 스크립트 작성하다보면, 자주 사용하게 되는 함수 존재
Ex. 아래의 예시들은 익스플로잇 과정에 거의 항상 필요

  • 정수를 리틀 엔디언의 바이트 배열로 바꾸는 패킹 함수
  • 그 역을 수행하는 언패킹 함수 등
  • 지난 코스에 사용한 get_shell() 함수의 주소를 리틀 엔디언을 적용하여 변환

위와 같은 함수들을 반복적으로 구현하는 것은 비효율적.
pwntools 라는 파이썬 모듈에 이런 것들을 모음

용어 정리

  • exploit : 보안 취약점을 이용한 공격.
  • payload : 사용에 있어서 전송되는 데이터. 보안에서는 멀웨어의 일부분.
  • 멀웨어 : 컴퓨터 또는 설치한 모든 소프트웨어에 해를 끼치려 고안한 소프트웨어
    민감한 정보를 훔치거나 사용자 모르게 사용자의 이메일 계정에서 가짜 메일 전송 가능
  • 엔디언 : 컴퓨터의 메모리와 같은 1차원 공간에 여러 개의 연속된 대상을 배열하는 방법
    바이트를 배열하는 방법을 특히 Byte Order 라 함.
    큰 단위가 앞에 나오는 Big-endian
    작은 단위가 앞에 나오는 Little-endian
    둘 모두 지원 & 두 경우에 속하지 않는 Middle-endian
  • 아키텍처 : 컴퓨터 시스템의 하드웨어 구조. CPU, Register, Memory, I/O 등에 대한
    전반적인 기계적 구조와 이를 설계하는 방법

파일 종류

종류설명
텍스트 파일1. 문자로 구성된 파일
2. 대부분 ASCII 문자로 이뤄진 파일 또는 사람이 사용하는 한글 영문 등과 같은 문장들로 이뤄진 파일
3. 소스코드 파일(.cpp), README.txt 등
바이너리 파일1. 데이터로 구성된 파일
2. 모든 파일은 0과 1로 이루어짐
3. 이미지파일(.png), 데이터파일(.dat), 실행파일(.exe) 등

pwntools API 사용법

공식 메뉴얼 pwntools

Process

로컬 프로세스를 실행하여 통신할 때 사용되는 클래스

p = process("binary file")

process 클래스는 로컬에서 바이너리를 실행할 때 환경 변수를 직접 설정할 수 있고,
프로그램을 실행할 때 인자를 전달해야할 경우 전달 가능

exploit 을 테스트하고 디버깅하기 위해 사용

< Dreamhack 예시 >

< haerinn 블로그 예시 >

이 예시는 로컬 파일시스템에 존재하는 /home/theori/binary 바이너리를 실행

Remote

원격 서비스에 접속하여 통신할 때 사용되는 클래스

p = remote("IP", port)

특정 주소에 열려있는 특정 포트에 TCP 연결을 맺는다.
연결이 성공적으로 맺어지면 remote 객체를 리턴

대상 서버를 실제로 공격하기 위해 사용

< Dreamhack 예시 >

< haerinn 블로그 예시 >

이 예시는 127.0.0.1 주소에 열려있는 5000번 포트에 TCP 연결을 맺음.

Send & Recv

언제 사용하는가?
소켓에 연결하거나 프로그램을 실행할 때 데이터를 보내고 읽어들이는 작업이 필요하다.
이때 사용하는 것이 send 와 recv

조건
연결이 맺어진 객체가 존재해야 함

send

연결이 맺어진 객체에 데이터를 보내는 메소드

p = remote("IP", port)
p.send("Data")

+ sendline : 연결이 맺어진 객체에 개행을 포함하는 데이터를 보내는 메소드
< haerinn 블로그 예시 >

위 예시는 127.0.0.1 의 22번 포트에 연결한 후 AAAA 를 전송

recv

연결이 맺어진 객체로부터 수신한 데이터를 리턴하는 메소드

p = remote("IP", Port)
print p.recv(byte)

< haerinn 블로그 예시 >

+ recvline : 연결이 맺어진 객체로부터 개행까지 수신하여 리턴하는 메소드
이때, 읽을 바이트 수를 지정해주지 않고 개행까지 읽음

+ recvuntil : 연결이 맺어진 객체에서 원하는 문자 혹은 문자열까지 읽는 메소드
< haerinn 블로그 예시 >

recv 와 recvn 의 차이

recv(n) : 최대 n 바이트를 받는 것 = n 만큼 받지 못해도 no error
recvn(n) : 정확히 n 바이트의 데이터를 받지 못하면 계속 대기

< Dreamhack 예시 >

Packing & Unpacking

Exploit 작성하다 보면 어떤 값을 리틀 엔디언의 바이트 배열로 변경하거나, 역의 과정을 거쳐야 하는 경우가 존재함. 이때 packing 과 unpacking 사용

즉, Packing 과 Unpacking 은 각각의 데이터 크기에 맞게 데이터를 변환할 때 사용

  • packing 함수 : 정수를 인자로 받아 패킹한 후 문자열 형태로 리턴
  • unpacking 함수 : 문자열을 인자로 받아 언패킹 한 후 정수 형태로 리턴

    두 함수는 리틀 엔디언 혹은 빅 엔디언 형태로 지정해줄 수 있고,
    지정하지 않는다면 리틀 엔디언 형태로 변환
    데이터 크기에 따라 함수가 존재하기 때문에 데이터에 맞게 사용해야 한다

pack

< Dreamhack 예시 >

직접 실행

위와 같은 결과를 얻었다.
패킹을 했을 때, little endian 의 형태로 나옴을 확인할 수 있었고, endian='big'을 통해
big endian 의 형태로 반환하는 방법을 적용해봤다.
재밌는 사실은
0x41424344 를 packing 한 결과는 ABCD 의 역순이었고,
ABCD 를 unpacking 한 결과는 1424344 의 역순이었다.
unpacking 에서는 hex 함수를 사용하는데 이는 unpacking 결과가 정수형이기 때문이다

의문

  • pakcing 했을 때 같이 출력되는 b 의 의미는 무엇일까?
  • 16 진수 0x... 형태에서 x 뒤 첫번째는 4로 고정되는데 무슨 의미일까?

< haerinn 블로그 예시 >
1. p8 : 1 바이트의 데이터를 패킹

0x41을 문자 형태로 변환하여 A를 리턴한 것을 확인

  1. p16 : 2 바이트의 데이터를 패킹

    0x4142 를 리틀 엔디언의 형태로 변환하여 "BA" 를 리턴한 것을 확인
  2. p32 : 4 바이트의 데이터를 패킹

    0x41424344 를 리틀 엔디언의 형태로 변환하여 "DCBA" 를 리턴한 것을 확인
  3. p64 : 8 바이트의 데이터를 패킹

unpack

< haerinn 블로그 예시 >
1. u8 : 1 바이트의 데이터를 언패킹

"A" 를 정수 형태로 변환하여 65 리턴 확인
2. u16 : 2 바이트의 데이터를 언패킹

"AB" 를 정수형태로 변환하여 16961 리턴 확인
hex 함수 사용하여 16진수로 변환하면 0x4241 인 것을 알 수 있다.
3. u32 : 4 바이트의 데이터를 언패킹

"ABCD" 를 정수형태로 변환하여 1145258561 를 리턴한 것 확인
hex 함수를 사용하여 16 진수로 변환하면 0x44434241
4. u64 : 8 바이트의 데이터를 언패킹

Interactive

셀을 획득했거나, exploit 의 특정 상황에 직접 입력을 주면서 출력을 확인하고 싶을 때 사용
호출하면 터미널로 프로세스에 데이터 입력 -> 프로세스의 출력 확인

ELF

ELF 헤더에는 exploit 에 사용될 수 있는 각종 정보가 기록
pwntools 사용하면 쉽게 참조 가능

Context.log

exploit 에 버그가 발생하면 exploit 도 디버깅해야 함.
pwntools 에는 디버그의 편의를 돕는 로깅 기능이 존재
로그 레벨은 context.log_level 변수로 조절 가능

Context.arch

pwntools 는 셀코드를 생성하거나, 코드를 어셈블, 디스어셈블하는 기능 등을 가지고 있음.
이들은 공격 대상의 아키텍처에 영향을 받음
따라서, pwntools 는 아키텍처 정보를 프로그래머가 지정할 수 있게 하며,
이 값에 따라 몇몇 함수들의 동작이 달라짐.

Shellcraft

pwntools 에는 자주 사용되는 셀 코드들이 저장 => 공격에 필요한 셀 코드를 쉽게 사용 가능

한계

  • 정적으로 생성된 셀 코드는 실행될 때의 메모리 상태를 반영하지 못함
  • 프로그램에 따라 입력할 수 있는 셀 코드의 길이나 구성 가능한 문자의 종류에 제한이 있을 수 있다. 이때, 이런 조건 반영하기에 어려움

여러 제약 조건이 존재하는 상황에서는 직접 셀 코드를 작성하는 것이 좋다

x86-64 대상으로 생성 가능한 셀 코드 종류 찾기

Asm

pwntools 는 어셈블 기능을 제공
대상 아키텍처가 중요 => 아키텍처를 미리 지정

pwntools 실습

rao exploit


위 내용이 rao 코드다.
내용은 이후 커리큘럼에서 배운다고하니 그냥 넘어감

이 실습의 결과로 나와야할 것은 아래와 같다.

처음 주어진 코드는 c 파일이다. 이를 컴파일해서 get_shell의 주소를 얻어야하며,
얻은 주소를 통해 아래의 py 코드를 수정해야함.

  1. gcc -o rao.c rao 를 이용하여 rao 실행 파일을 만들어준다.
  2. gdb rao 를 통해 pwndbg 에 접속
  3. pwndbg 에서 print get_shell 을 하고, 이를 통해 shell 의 주소를 얻는다.
  4. 얻은 주소의 값을 rao.py 의 'get_shell' 변수에 저장해준다.
    ( 즉, 위 예시에서 변경되는 값은 0x4005a7 이다 )
profile
UOU_Unknown

0개의 댓글