#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char* argv[], char* envp[]){
printf("Welcome to pwnable.kr\n");
printf("Let's see if you know how to give input to program\n");
printf("Just give me correct inputs then you will get the flag :)\n");
// argv
if(argc != 100) return 0;
if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
printf("Stage 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
printf("Stage 2 clear!\n");
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
// file
FILE* fp = fopen("\x0a", "r");
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0;
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
fclose(fp);
printf("Stage 4 clear!\n");
// network
int sd, cd;
struct sockaddr_in saddr, caddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
printf("bind error, use another port\n");
return 1;
}
listen(sd, 1);
int c = sizeof(struct sockaddr_in);
cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
if(cd < 0){
printf("accept error, tell admin\n");
return 0;
}
if( recv(cd, buf, 4, 0) != 4 ) return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
printf("Stage 5 clear!\n");
// here's your flag
system("/bin/cat flag");
return 0;
}
코드가 엄청 길어보였는데, 막상 포스팅하면서 보니 70줄도 안 됨...
각각의 인풋들을 다른 방식으로 전달해주면 플래그를 읽어주는 방식인데
우선 pwnable.kr 풀면서 tmp폴더에서 내 파일을 쓸 수 있다는 것도 처음 알았다. ㅋ
그 전에는 그냥 페이로드를 바로 명령어 창에다가 입력하는 형식으로만 했는데, 여기서는 그렇게 하는 방법 모르겠음 ㅠ
맨 처음에 stage 1을 간단히 풀고, stage 2에서부터 막혔는데,
코딩을 할 수 있으면 fd (file descriptor)를 사용해서 했겠지만, 그냥 바로 주려고 하니까 문제가 된 게,
파이프를 쓰면 표준 출력(1)을 표준 입력(0)으로 바꿔주고 리다이렉션을 1>&2 이런 식으로 사용하려고 해도
파이프 없이 exec 파일에다가 전해주는 방법을 모르겠어서 엄청 삽질했음
그러다 검색해보니까 다들 파이썬 코드 쓰고 있음; 뭐지 하고 보다가 tmp 파일에 할 수 있단 걸 알고,
이번에 처음으로 pwntools 모듈을 조금 사용해보면서 여러가지를 배웠음
ㅋㅋㅋ 아 그리고 tmp폴더다 보니, 작성은 가능한데 파일이 날라가기도 함...
생각도 안하고 있다가 갑자기 날라가서 다 다시 작성함
덕분에 연습은 더 된 것 같기도 하고...
from pwn import *
argv_myste=[str(i) for i in range(100)]
argv_myste[ord('A')]="\x00"
argv_myste[ord('B')]="\x20\x0a\x0d"
argv_myste[ord('C')]="4567"
with open("./stderr_myste", "w") as f:
f.write("\x00\x0a\x02\xff")
err_fd = open("./stderr_myste", "r")
env_myste={"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}
with open("\x0a", "w") as f:
f.write("\x00\x00\x00\x00")
s = process(executable="/home/input2/input", argv=argv_myste, stderr=err_fd, env=env_myste)
s.send("\x00\x0a\x00\xff")
r=remote('127.0.0.1', 4567)
r.send("\xde\xad\xbe\xef")
r.close()
s.interactive()
err_fd.close()
위의 파이썬 코드가 내 페이로드.
처음에 어떻게 작성하는지만 살짝 보고 내가 직접 작성하긴 했음 ㅠ
env 값 설정할 때 보면 json 처럼 하는데, dict(dictionary) 자료형(파이썬도 구조체라고 해야하나?)을 사용한다고 함...
파이썬은 매번 검색하면서만 사용해서 그냥 json 데이터로 생각해도 될 듯 함
추가) 키 : 값 순서쌍의 '집합'
선언 방식은 저렇게 말고도 몇 가지 더 있고, 검색하면 다 나옴 ㅅㅅ
pwntools 모듈의 process와 remote 함수는 앞으로도 많이 사용할 것처럼 보였음
pwntools process라고 치면 모듈 documents 바로 나오니까 거기서 필요한 인자들 찾고 풀 수 있음
근데 여기서 process를 열면 input 파일이 열려있는 위치가 현재 내 pwd이기 때문에,
file open을 하는 것도 그렇고 제일 마지막에 cat flag도! 내 위치에서 함
그래서 stage 4도 내가 같은 이름의 파일 만들어서 넘길 수 있고
마지막 flag 읽을 때는 symbolic link 만들어놔줘야지 제대로 읽힘
계속 스테이지 클리어만 나오고 플래그 안 주길래 이건 또 뭔가 했음 ㅠ
'Pwnable.kr' 카테고리의 다른 글
Pwnable.kr [mistake] (0) | 2019.05.01 |
---|---|
Pwnable.kr [leg] (0) | 2019.05.01 |
Pwnable.kr [random] (0) | 2019.04.29 |
Pwanble.kr [passcode] (0) | 2019.04.29 |
Pwnable.kr [bof] (0) | 2019.04.24 |