본문 바로가기

Pwnable.kr

Pwnable.kr [input]

#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