Writer - hoodbilen

pwntools란?


시스템해킹(pwnable)에 필요한 기능들을 최대한 집대성해 만든 파이썬 모듈이다. 실제로는 프레임워크라고 더 자주 표현한다.

How to install?


$ apt-get update
$ apt-get install python2.7 python-pip python-dev git libssl-dev libffi-dev build-essential
$ pip install --upgrade pip
$ pip install --upgrade pwntools

추가 정보는 아래 확인.

https://github.com/Gallopsled/pwntools

USE


$ vi test.py
$ python test.py

이런 식으로 작성하고, 실행시킬 수 있다.

process & remote


process함수는 로컬 바이너리에 대해 익스플로잇을 테스팅해볼 때 사용하는 함수이고, remote함수는 원격 서버를 대상으로 실제 익스플로잇을 작동시킬 때 사용하는 함수이다.

from pwn import *
p = process('./test') # local 파일 test를 대상으로 exploit 실행함.
p = remote('warroom.kr', 123456) # warroom.kr의 123456번 포트에 있는 바이너리를 대상으로 exloit 실행함.

send


pwntools에는 send와 관련된 다양한 함수가 있다.

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

p.send('A') # ./test에 'A'를 입력한다.
p.sendline('A') # ./test에 'A'+'\n'을 입력한다.
p.sendafter('hello', 'A') # ./tets가 'hello'를 출력하면, 'A'를 입력한다.
p.sendlineafter('hello', 'A') # ./test가 'hello'를 출력하면 'A'+'\n'을 입력한다.

send, sendline, sendafter, sendlineafter 을 사용할 때는 process값을 받은 p.를 꼭 붙여주어야 한다.

recv


pwntools에는 recv와 관련된 다양한 함수가 있다.

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

data = p.recv(1024) # p가 출력하는 데이터중 최대 1024바이트의 데이터를 받아서 data에 저장
data = p.recvn(5) # p가 출력하는 데이터중 정확히 5바이트를 받아서 data에 저장
data = p.recvline() # p가 출력하는 데이터중 개행문자를 만날 때 까지를 data에 저장
data = p.recvuntil('hello') # hello라는 문자열을 p가 출력할 때 까지 받아서 data에 저장
data = p.recvall() # 연결이 끊어지거나 프로세스가 종료될 때까지 받아서 data에 저장

주의해서 봐야할 것은 p.recvp.recvn 의 차이점인데, p.recv()는 최대 n 바이트를 받는 것 이기 때문에, 1024바이트를 다 채워 받지 못해도 에러를 발생시키지 않지만, p.recvn()의 경우 정확히 인자만큼의 데이터를 받지 못하면 계속 대기한다.

recv,recvline,recvn,recvuntil, recvall은 불필요한 문자까지 입력받은 후 send를 할때도 사용할 수 있다. 불필요한 문자를 입력받을 때는 data = 를 제외하고 p.recvline() 처럼 단독으로 사용하면 된다.

packing & unpacking


대부분의 CPU는 little endian을 이용한다. 익스플로잇을 작성하다 보면 어떤 값을 little endian스트링으로 변경하거나, 또는 역의 과정을 거쳐야 하는 경우가 자주 있는데, 이를 쉽게 할 수 있도록 pwntools에서는 함수로 제공한다.

ex)

#test.py
from pwn import *

data32 = 0x41424344
data64 = 0x4142434445464748

print p32(data32)
print p64(data64)

data32 = "ABCD"
data64 = "ABCDEFGH"

print hex(u32(data32))
print hex(u64(data64))
$python test.py
DCBA
HGFEDCBA
0x44434241
0x4847464544434241

log


from pwn import *
context.log_level = 'debug' # 서버 혹은 바이너리와 이 익스플로잇간에 오고가는 모든 데이터를 화면에 hexdump형식으로 출력해줌

debug


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

익스플로잇을 실행하는 중간에 debug를 하고 싶을때 원하는 위치에서 위 명령어를 사용하면 새 창에서 해당 프로그램이 gdb로 보여진다. 정확하게 원하는 값이 채워졌는지, 릭이 정확하게 되었는지, 스택주소, 데이터 등을 볼 때 유용하다.

interactive


쉘을 획득한 경우이거나, 익스플로잇의 특정 경우에 직접 입력을 주면서 디버깅을 해보고 싶다면 사용하게 되는 함수. 익스플로잇과 프로세스와의 연결을 stdin, stdout <=> process로 바꿔준다.

pwntools 익스플로잇이 끝났을 때 쉘을 따지 않아도 $ 가 나오는 이유가 이것 때문이다.

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

ELF


리눅스에서 작동하는 실행파일 ELF 에는 각종 정보가 기록되어 있다. 그 중 대표적인 것이 GOT, PLT 인데, pwntools를 통해 이런 값들을 쉽게 구해올 수 있다.

from pwn import *
e = ELF('./test')
puts_plt = e.plt['puts'] # ELF ./test에서 puts()의 PLT주소를 찾아서 puts_plt에 넣는다.
read_got = e.got['read'] # ELF ./test에서 read()의 GOT주소를 찾아서 read_got에 넣는다.

해당 문서는 pwntools를 사용하면서 자주 마주치는 함수들에 대해서만 간략히 소개한 문서이다.

자세한 사용법을 알고싶은 사람은 아래 공식 주소를 참고.

https://docs.pwntools.com/en/stable/index.html

'hacking > tool' 카테고리의 다른 글

mac IDA create structure  (0) 2019.08.08

IDA는 이런식으로 Structures 창이 있다. 해당 창에서 구조체를 선언하면 code를 좀더 편하게 볼 수 있다.

구조체를 선언하는 방법은 insert key를 누르는 것이다. 그런데 mac은 자판에 insert key가 없다... 대체로 `Insert -> fn+Enter`라고 했는데 아무리 눌러도 되지를 않는다... 해서 알아보니 mac은 단축키가 따로 존재하더라.

바로 `i`다. 

`i`를 누르면 이런식으로 create할 수 있는 창이 생긴다. 해당 창에서 내가 원하는 구조체 명을 적고 `ok`를 누르면 된다.

이런식으로 구조체의 ends를 누르고 `d`key를 누르면 변수가 선언된다.

선언된 변수에서 `n`key를 누르면 변수 명을 바꿀 수 있다.

선언된 변수에서 `d`key를 누르면 변수 크기를 바꿀 수 있다.

선언된 변수에서 `fn+u`key를 누르면 변수를 삭제할 수 있다.

 

구조체를 다 만들었으면 코드에 삽입해주면 된다.

변경하고 싶은 부분을 클릭하고 `y`를 누르면 해당 부분을 재정의 할 수 있다. 이런식으로 재정의해주면 된다.

'hacking > tool' 카테고리의 다른 글

How to use pwntools  (0) 2019.08.08

uaf관련 ctf문제가 처음이여서 굉장히 많이 삽질했다.

 

우선 main문을 보면 6개 정도의 case가 존재하는 것 같다.

create_pet_recode함수를 보면 pet recode를 생성하는 함수인 것 같다. 

PET은 구조체이고, 이런식으로 되어있는 것 같다.(char *name, char *kind, int age)

edit_pet_record함수는 이미 존재하는 record의 id를 가져와 내용을 변경해주는 함수인 것 같다. 그런데 마지막 조건문을 보면 'n'일 경우 `::ptr`(전역변수)을 free해준다. 하지만 `::ptr`=NULL; 하는 부분이 없음으로 `::ptr`은 여전히 free된 chunk주소를 가르키고 있다. 여기서 uaf가 발생한다.

 

heap구조를 분석해보자.

`gdb -q Cat`을 입력하고 `r`을 한 뒤,

이런식으로 입력하고, `control+c`를 하면 프로그램이 멈추면서 디버깅을 할 수 있다.

`heapinfo`를 이용해 fastbin구조를 볼 수 있다.

free된 chunk 주소가 `0x603000`근처임으로 해당 주소를 보면 heap구조를 볼 수 있다.

heap구조

----------------

&AAAA, &BBBB

----------------

AAAA

----------------

BBBB

----------------

0

----------------

&0x603060

----------------

&0x603080

----------------

top chunk

----------------

이제 create_pet_recode함수를 이용해 다시 `malloc`을 불러오면 어떻게 구조가 변하는지 보자.

이런식으로 입력한 뒤

heap구조를 보면

heap구조

----------------

&AAAA, &BBBB

----------------

AAAA

----------------

BBBB

----------------

DDDD

----------------

CCCC

----------------

&CCCC, &DDDD

----------------

top chunk

----------------

이런식으로 값이 들어간다. (fastbin은 LIFO구조임으로.)

여기서 다시 edit_pet_record함수를 접근하면 `::ptr`은 아까와 똑같은 chunk구조를 가지고 있다고 생각하며 동작한다.(heap구조 맨 위 &AAAA, &BBBB를 생각하며 이해) 그 결과 값 `DDDD`를 주소로 인식해 `&DDDD`로 접근해 값을 대입하고, 그 뒤로 텅 비어있음으로 &0000에 접근해 값을 대입하려다 실패하고 `Segmentation fault (core dumped)`를 띄운다.

이제 모든 분석이 끝났다. 공격 시나리오는 두번째 create_pet_recode함수호출에서 kind위치에 PET[3](id = 2)의 주소+bss공간 주소를 넣은 뒤, edit_pet_record함수를 호출해 name위치에 bss공간주소를 넣고, kind위치에 잘 안쓰이는 puts함수 got주소를 넣어주자.

그 후 print_record함수를 호출해(case 3) id에 2을 입력하면 name에 puts_got실제주소가 출력되 leak이 가능하다.

다시 create_pet_recode함수를 호출하고 kind위치에 atoi함수의 got주소를 넣고, 모양을 맞춰주기 위해 대충 bss공간 주소를 넣어준 뒤, edit_pet_record함수를 호출해 name위치에 leak한 system_got값을 넣고, bss공간주소를 대충 넣어주면 될 것같다.

 

IDA를 보면 0x6020A0에 PET배열이 들어가는 것을 볼 수 있다. 

보면 id = 2의 주소가 0x6020b0인 것을 알 수 있다.

이런식으로 payload를 작성했다....

+ Recent posts