1. pwntools 사용하기 위해 pwn 모듈 import
from pwn import *
2. process 클래스의 인스턴스를 만들며, elf 파일 실행할 수 있음.
r = process("파일 이름")
-> 해당 프로그램 실행시키고, 입력 대기함.
-> 이후, 실행된 프로그램에는 r 변수를 통해 접근 가능.
-> process() 로 실행시킨 프로그램은 입출력을 화면에 직접 할 수 없음.
-> r 변수를 통해 함수를 호출시키면서 파이썬 코드를 통해 프로그램의 stdin에 입력을 넣어주고, stdout에서 출력을 받아올 수 있음.
3. 접속
-process : 로컬 바이너리에 대해 익스플로잇 실험 시 사용하는 함수
p = process('./test') # process(filename)
- remote : 원격 서버 대상으로 실제 익스플로잇 작동시 사용
p = remote('dokhakdubini.tistory.com', 123456) # remote(host, port)
- ssh : ssh를 통해 접속하는 함수
p = ssh("fd", "pwnable.kr", 2222) # ssh(user, host, port, password) 순서
-접속 종료
p.close() # 접속 종료
4. 페이로드 전송 (send)
from pwn import *
p = process('./test')
p.send('A') # ./test에 'A'입력
p.sendline('A') # ./test에 'A'입력 뒤에 newline character(\n)까지 입력
p.sendafter('asdf', 'A') # ./test가 'asdf'를 출력할 시, 'A'를 입력한다
p.sendlineafter('asdf', 'A') # ./test가 'asdf'를 출력할 시, 'A'+'\n'를 입력한다
- send함수를 이용할 때는 반드시 앞에 어떤걸 process나 remote했는지 붙여야 함 -> p. 붙임
5. 데이터 받기 (recv)
from pwn import *
p = process('./test')
data = p.recv(1024) # p가 출력하는 데이터 중 최대 1024바이트의 데이터를 받아서 data에 저장
data = p.recvline() # p가 출력하는 데이터 중 개행문자를 만날 때까지를 data에 저장
p.recvn(5) # p가 출력하는 데이터 중 정확히 5바이트 받아서 data에 저장
print p.recvuntil('asdf') # 'asdf'라는 문자열을 p가 출력할 때까지 받아서 출력
print p.recvall() # 연결이 끊어지거나 프로세스가 종료될 때까지 받아서 data에 저장
- 변수에 직접 받아오는 방법
> 따로 결과값을 저장하고 싶을 때 사용하고, 출력시키고싶으면 print data의 식으로 출력
- 단순히 recv를 실행시키는 방법
> 필요없는 데이터를 받을 때 사용
- print p.recv() 실행하는 방법
> 받은 값 출력
* p.recv와 p.recvn의 차이
- p.recv() : 최대 n바이트를 받는 것이기 때문에, 예시의 경우 1024바이트를 모두 다 채워 받지 못해도 에러를 발생시키지 않음.
- p.recvn() : 정확히 인자만큼의 데이터를 받지 못하면 계속 대기함.
-주로 print p.recv(1024)의 방식 사용.
6. 라이브러리/바이너리 - ELF / run
from pwn import *
p = process('./test')
e = ELF('./test') # e에 test의 라이브러리를 불러옴
sh = p.run('/bin/sh') # test로 /bin/sh를 바이너리에 실행시킴
sh.sendline("whoami") # "echo hi"를 전송(권한이 있다는 전제 하에)
puts_plt = e.plt['puts'] # ELF ./test에서 puts()의 PLT주소를 찾아서 puts_plt에 넣는다.
read_got = e.got['read'] # ELF ./test에서 read()의 GOT주소를 찾아서 read_got에 넣는다.
- 리눅스에서 작동하는 실행파일에서 ELF에는 각종 정보가 기록되어 있음.
- 대표적으로 GOT, PLT등이 있는데, pwntools를 통해 쉽게 구할 수 있음.
- run : 바이너리 실행시킬 수 있음.
7. 패킹 - p32, p64 / u32, u64
'''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))
실행 결과
sik@ubuntu:~$ vi test.py
sik@ubuntu:~$ python test.py
DCBA
HGFEDCBA
0x44434241
0x4847464544434241
p32 : 32비트 리틀 엔디안 방식으로 패킹해주는 함수 (p32(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)
p64 : 64비트 리틀 엔디안 방식으로 패킹해주는 함수 (p64(value, endian='big')을 하면 빅 엔디안으로 패킹해준다)
u32(str) : 32비트 리틀 엔티안 방식으로 언패킹해주는 함수 (반환값은 int형이다 그리고 str에는 패킹된 string이 들어가야 한다.)
u64(str) : 64비트 리틀 엔티안 방식으로 언패킹해주는 함수 (반환값은 int형이다 그리고 str에는 패킹된 string이 들어가야 한다.)
8. debug - gdb.attach
- 페이로드를 진행하는 도중 gdb를 실행시킬 수 있는 함수
import pwn from *
p = process('./test')
gdb.attach(p)
- 실행 시, 새 창에서 해당 프로그램이 gdb로 보여짐.
- 그러나 서버 접속으로는 불가능하고, process(파일 실행)로만 가능.
9. 쉘 접속 - interactive
from pwn import *
p = process('./test')
p.interactive()
- interactive() : 쉘을 획득한 경우이거나, 익스플로잇 시 직접 입력을 주면서 디버깅이 필요한 경우 이용하는 함수
- 익스플로잇 파일과 프로세스와의 연결을 stdin/stdout에서 process로 바꿔줌.
- pwntools동작이 끝난 뒤 $가 나오는 이유도 이 함수 때문.
실습 - pwntools 사용하기
v다음 프로그램을 작성 후 컴파일
gcc –o hello hello.c
#include <stdio.h>
void main(){
printf("hello\n");
char buf[10];
scanf("%10s", buf);
if(!strcmp(buf, "test123")){
printf("pwntool test ok\n");
}
else{
printf("not working\n");
}
}
v다음 프로그램을 작성 후 컴파일 (x.py)
from pwn import *
r = process('./hello')
print (r.recv(100))
r.sendline('test123')
print (r.recv(100))
vPwntools 사용하기
python3 x.py
아래처럼 pwntool test ok 가 나오면 성공.
네트워크를 대상으로 데이터 주고받기
- Process 대신 ‘remote’ 를 이용
아래와 같이 IP 주소, 포트번호를 전달한 뒤 동일하게 이용
\xcc\xaa\xbb를 입력해야 할 때
[출처]
security-nanglam.tistory.com/155
'수업 > pwnable' 카테고리의 다른 글
[기말] 커맨드 인젝션 (0) | 2021.06.07 |
---|---|
10주차 이론 (0) | 2021.06.06 |
[기말] 쉘코드(ShellCode) (0) | 2021.06.05 |
엔디안 (Endianess) (0) | 2021.06.05 |
메모리 구조 - 스택(stack) (0) | 2021.06.05 |