본문 바로가기
Security/pwnable.kr

[pwnable.kr] Toddler's Bottle - input

by 단월໒꒱ 2022. 5. 5.

이번에는 input 문제를 풀어보았다.

 

 

 

 

그냥 컴퓨터 프로그램에 input을 어떻게 전달할 수 있냐고 물어보고 있다.

별 의미는 없는 것 같다.

 

아래에 있는 명령어를 통해 접속해준다.

 

 

 

 

접속 완료했다.

 

 

 

 

파일 리스트는 이 정도 있다는 것을 확인한 뒤 cat으로 input.c 내용을 살펴보았다.

 

 

 

 

너무 길어서 캡쳐보다는 코드를 긁어와서 아래에 가져왔다.

 

 

#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;
}

 

 

코드를 보니 단계별로 진행하는 것 같아 단계별로 쪼개보았다.

 

 

1단계)

 

 

    // 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");

 

 

명령인수에 대한 내용이다.

요약하자면, 인자의 개수가 100개이고, 65번째 인자가 "\x00", 66번째 인자가 "\x20\x0a\x0d" 이면 된다.

 

 

2단계)

 

 

	// 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");

 

 

표준 입출력에 대한 내용이다.

stdin의 4btye에 "\x00\x0a\x00\xff", stderr의 4btye에 "\x00\x0a\x02\xff"를 줘야한다.

stdin은 send로 보내도 되는데, stderr은 파일을 따로 만들어줘야한다.

 

 

3단계)

 

 

	// env
	if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
	printf("Stage 3 clear!\n");

 

 

환경변수에 대한 내용이다.

getenv 함수는 환경변수를 가져오는 함수이다.

환경변수 "\xde\xad\xbe\xef"의 값이 "\xca\xfe\xba\xbe"와 같아야 한다.

 

 

4단계)

 

 

	// 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");

 

 

파일에 대한 내용이다.

"\x0a"라는 파일을 여는데, 이 파일의 처음 4byte가 "\x00\x00\x00\x00"이면 된다.

이것도 파일을 따로 만들어줘야한다.

 

 

5단계)

 

 

	// 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");

 

 

네트워크에 대한 내용이다.

요약하자면, argv[67]에 해당하는 포트를 열고, 여기서 4btyet를 받아, 이 값이 "\xde\xad\xbe\xef"가 되면 된다.

 

 

 

위의 내용들을 토대로 익스플로잇 코드를 작성하면 아래와 같다.

 

 

 

from pwn import *

# stage 1
argvs = [str(i) for i in range(100)]
argvs[65] = "\x00"
argvs[66] = "\x20\x0a\x0d"

# stage 2
with open("./stderr", "a") as f:
    f.write("\x00\x0a\x02\xff")

# stage 3
envVal = {"\xde\xad\xbe\xef" : "\xca\xfe\xba\xbe"}

# stage 4
with open('./\x0a', 'a') as f:
    f.write("\x00\x00\x00\x00")

# stage 5
argvs[67] = "11111"

p = process(executable = "/home/input2/input", argv=argvs, stderr=open("./stderr"), env=envVal)

p.sendline("\x00\x0a\x00\xff")

local = remote("localhost","11111")
local.send("\xde\xad\xbe\xef")

p.interactive()

 

 

이 익스플로잇 코드를 아무데나 두면 안되고

 

 

 

 

tmp 디렉터리 아래에 아무 폴더나 만든 뒤에 거기에 익스플로잇 파일을 생성해줘야 한다.

 

생성한 뒤 그냥 실행하면 flag와 연결이 안되서 제대로 작동하지 않기 때문에 심볼릭 링크를 걸어줘야 한다.

 

 

 

 

이렇게 걸어준다.

 

 

작성한 익스플로잇코드를 실행시키면

 

 

 

 

성공적으로 flag를 획득할 수 있다.

 

획득한 flag를 입력해야하는데, 현재 pwnable.kr 페이지에 접속이 불가능해서 나중에 입력하고 수정하겠다.

 

댓글