지금까지 간단간단한 문제들은 필요가 없었지만, 앞으로 나올 문제들을 푸는데 쉽고 간편해지기 때문에 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를 사용하기 위해 pwn모듈을 임포트해줍니다.
from pwn import *
### ssh는 ssh("username", "ip", port=, password="")
shp = ssh("DPOS", "localhost", port=22, password="abcd1234")
### nc는 remote(ip, port)
ncp = remote("localhost", 8888)
### localfile의 경우는 process(path)
lfp = process("/home/dpos/문서/sample")
### 데이터 받기
p.recv(byte)
# 원하는 byte만큼 데이터를 가져오는데 공란일 경우엔 데이터 전체를 가져옴
p.recvline()
# 데이터 한줄을 가져올 때 사용
p.recvuntil('A')
# 데이터를 A라는 문자를 찾을 때까지 받아오고, \n을 할 경우 recvline()과 같은 결과
### 데이터 전송
p.send(data)
# 데이터를 전송하는데 보통 read()에 사용
p.sendline(data)
# 데이터를 라인단위로 보내는데 보통 scanf(), puts(), gets()에 사용 + 마지막에 /n 추가
### 유저 & 쉘 입출력
p.interactive()
# 사용자가 쉘과 직접 데이터를 주고 받음
### packing
# 32bit 패킹
var = p32(0x12345678) # var = \x78\x56\x34\x12 or xV4\x12
var = p32(0xABCD) # var = \x68\x67\x66\x65 or \xcd\xab\x00\x00
# 64bit 패킹
var = p64(0x12345678) # var = \x00\x00\x00\x00\x78\x56\x34\x12 or xV4\x12\x00\x00\x00\x00
# big endian으로 패킹
var = p32(0x12345678, endian ='big') # var = \x124Vx
var = p64(0x12345678, endian ='big') # var = \x00\x00\x00\x00\x124Vx
### unpacking
# 32bit 언패킹
var = u32("\x78\x56\x34\x12") # var = 305419896 -(hex)-> (0x12345678)
var = u32("xV4\x12")
var = u32("\x68\x67\x66\x65") # var = 1701209960 -(hex)-> (0x65666768)
var = u32("\xcd\xab\x00\x00")
# 64bit 언패킹
var = u64("xV4\x12\x00\x00\x00\x00") # var = 305419896
var = u64("\x00\x00\x00\x00\x78\x56\x34\x12")
직접해보면 상상했던 \x78\x56\x34\x12
형태가 아니라 xV4\x12
이렇게 나오는 것을 볼 수 있는데 실제 사용에는 별 다른 문제가 없어서 그냥 사용하시면 됩니다. 정확한 값은 b'xV4\x12'
로 나올텐데 아스키코드로 \x78 = x
, \x56 = V
, \x34 = 4
이렇게 각각 변환되어 나오는거라 실제 사용시에는 문제 없이 굴러갑니다.
그럼 이제 사용하는 방법도 알아봤으니 실제로 사용해보도록합시다.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a, b, c;
int i = 1000;
srand(time(0));
while(i)
{
a = rand() % 5000;
b = rand() % 5000;
printf ("%d + %d = \n", a, b);
printf (">>> ");
scanf("%d", &c);
if( a + b == c ) { printf("PASS\n"); }
else { return 0; }
i--;
}
printf("\nDH{FLAG}\n\n");
return 0;
}
위와 같은 코드가 있을때 다음과 같은 스크립트를 짤 수 있습니다.
from pwn import *
r = process("./DPOS") # DPOS실행 파일에 접근
for i in range(1000):
ra = r.recvuntil("+")[:-2] # "a +"를 슬라이싱해서 a만 얻음
a = int(rawa)
rb = r.recvuntil("=")[1:-2] # " b ="을 마찬가지로 순수한 값만 얻음
b = int(rawb)
res = a + b
r.sendline(str(res)) # scanf이기 때문에 한줄 입력값을 보냄
r.recvuntil("PASS\n") # "PASS\n"를 읽어옴
r.interactive() # 유저에게 입출력 제어권을 넘김
이렇게 스크립트를 짜고 실행을 해보면
나름 플래그인 값이 나오게 됩니다. 일단 이 예제가 제 마음에 들진 않지만 당장 생각나는 문제도 없고, 제 컴퓨터 상대로 bof를 하고 싶진 않아서 생각 가능한 선에서 더 적합하다 생각되는 예제가 있으면 바꿀 예정입니다.
あまりにも簡単な ww