Featured image of post pwnable.kr input

pwnable.kr input

문제를 접속하면 아래의 파일이 주어진다. 우선 c파일을 열어보자. 열어보면 총 5개의 stage를 넘어가야하는데. 입력값을 코드에 맞춰 넘겨주면 된다. 각 스테이지 별로 분석을 해보자.

input2@pwnable:~$ ls
flag  input  input.c
input2@pwnable:~$ 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;
}

우선 stage1 을 보면 아래와 같다.

	// 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개이고, A,B번째 인자가 “\x00”, “\x20\x0a\x0d” 이어야한다.

다음으로 stage2를 보면 다음과 같다.

	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에서 4바이트를 읽어와서 내용이 \x00\x0a\x00\xff인지 확인하고 stderr에서 4바이트를 읽어와서 내용이 \x00\x0a\x02\xff이어야 한다.

다음으로 stage3을 보자.

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

환경변수의 값을 \xca\xfe\xba\xbe로 지정해야한다.

다음으로 stage4를 보자.

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라는 파일에 4방비트를 \x00으로 채워야 한다.

마지막으로 stage5를 보자

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[‘C’]에서 포트를 주고, \xde\xad\xbe\xef를 입력하면 되는 것을 알 수 있다. 이제 이걸 모아서 아래와 같은 exploit코드를 작성하고 실행 해주면 flag를 획득 할 수 있다.

  • flag파일이 없기 때문에 ‘ln -s /home/input2/flag flag’ 실행하여 링크 파일을 만들어 주어야 한다.
from pwn import *

argv_ans = [str(i) for i in range(100)]
argv_ans[ord('A')] = '\x00'
argv_ans[ord('B')] = '\x20\x0a\x0d'

with open('./stderr', 'a') as f:
    f.write('\x00\x0a\x02\xff');

env_ans = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}

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

argv_ans[ord('C')] = '9999'

target = process(executable='/home/input2/input',argv=argv_ans,stderr=open('./stderr'),env=env_ans)

target.sendline('\x00\x0a\x00\xff')

local = remote('localhost','9999')
local.send('\xde\xad\xbe\xef')

target.interactive()
input2@pwnable:/tmp/iptest$ python exploit.py
[+] Starting local process '/home/input2/input': pid 5668
[+] Opening connection to localhost on port 9999: Done
[*] Switching to interactive mode
Welcome to pwnable.kr
Let's see if you know how to give input to program
Just give me correct inputs then you will get the flag :)
Stage 1 clear!
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
Mommy! I learned how to pass various input in Linux :)
comments powered by Disqus