Input

2022. 3. 31. 12:42Pwnable.kr

 

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

 

총 5개의 스테이지로 구성되어 있습니다.

 

각 스테이지별로 설명을 하겠습니다.

	// 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"가 들어있는지 확인하는 문제입니다.

 

pwntools를 사용하여 인자들을 전달하여 통과하였습니다.

from pwn import *

context.log_level = 'debug'

s = ssh(user='input2', host='pwnable.kr', port=2222, password='guest')

argv = ['A' for _ in range(100)]
argv[0] = '/home/input2/input'
argv[65] = b'\x00' 
argv[66] = b'\x20\x0a\x0d'

p = s.process(executable=argv[0], argv=argv)

p.recv()

 

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

두 번째 스테이지입니다.

 

read 함수가 두 번 실행되는데 stdin, stderr로 부터 데이터를 4byte씩 읽어와 조건문과 비교하는 문제입니다.

stdin 경우 "\x00\x0a\x00\xff"를 입력하면 되지만 stderr 경우에는 "\x00\x0a\x02\xff" 데이터가 저장된 임의의 파일을 stderr로 연결해주어야 됩니다.

ssh.run, write 메서드를 사용하여 풀었습니다.

# stage 2
s.run("mkdir /tmp/pwnpwn")
s.write("/tmp/pwnpwn/stderr", "\x00\x0a\x02\xff")

# stage 1
argv = ['A' for _ in range(100)]
argv[0] = '/home/input2/input'
argv[65] = b'\x00' 
argv[66] = b'\x20\x0a\x0d'

p = s.process(executable=argv[0], argv=argv, stderr='/tmp/pwnpwn/stderr')

p.recv()

# stage 2
p.sendline(b"\x00\x0a\x00\xff")

 

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

세 번째 스테이지입니다. 환경변수 "\xca\xfe\ba\be"의 값이 "\xde\xad\xbe\xef"인지 확인합니다.

 

process env 매개변수에 리스트 형식으로 전달하면 풀 수 있습니다.

 

# stage 3
env = {b'\xde\xad\xbe\xef' : b'\xca\xfe\xba\xbe'}

p = s.process(executable=argv[0], argv=argv, stderr='/tmp/pwnpwn/stderr', env=env)

 

	// 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"인지 확인하는 스테이지입니다.

/home/input2 폴더에서는 파일을 생성할 수 없습니다. 그래서 파일을 생성할 수 있는 /tmp 디렉터리에서 파일을 만든 뒤 process 매개변수 중  현재 작업 디렉터리를 설정할 수 있는 cwd를 /tmp/pwnpwn로 바꾸면 /tmp/pwnpwn/0xa 파일을 읽어와 조건문을 통과할 수 있습니다.

 

# stage 4
s.write(b"/tmp/pwnpwn/\x0a", b"\x00\x00\x00\x00")

p = s.process(executable=argv[0], argv=argv, stderr='/tmp/pwnpwn/stderr', env=env, cwd='/tmp/pwnpwn')

 

	// 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 ['C'] 값을 포트번호를 하여 소켓 서버를 엽니다. 이후 입력받은 4byte의 데이터가 "\xde\xad\xbe\xef"인지 확인한 후 참이면 FLAG를 출력합니다.

 

# stage 5 
argv[67] = '56248'

# stage 5
r = s.remote('localhost', 56248)
r.send(b"\xde\xad\xbe\xef")

 

Stage 5까지 통과했지만 FLAG를 출력하지 않습니다. 그 이유는 현재 디렉터리는 /tmp/pwnpwn인데 FLAG는 /home/input2에 존재합니다. 그래서 cat flag를 하면 파일을 읽을 수 없습니다.

그래서 심볼릭 링크를 이용해 input2 디렉토리에 있는 flag를 /tmp/pwnpwn/flag가 가리키도록 하면 FLAG를 읽을 수 있습니다.

 

최종 코드와 결과는 아래와 같습니다.

from pwn import *

context.log_level = 'debug'

s = ssh(user='input2', host='pwnable.kr', port=2222, password='guest')

# stage 2
s.run("mkdir /tmp/pwnpwn")
s.write("/tmp/pwnpwn/stderr", "\x00\x0a\x02\xff")

# final 
s.run("ln -s /home/input2/flag /tmp/pwnpwn/flag")

# stage 4
s.write(b"/tmp/pwnpwn/\x0a", b"\x00\x00\x00\x00")

# stage 1
argv = ['A' for _ in range(100)]
argv[0] = '/home/input2/input'
argv[65] = b'\x00' 
argv[66] = b'\x20\x0a\x0d'

# stage 5 
argv[67] = '56248'

# stage 3
env = {b'\xde\xad\xbe\xef' : b'\xca\xfe\xba\xbe'}

p = s.process(executable=argv[0], argv=argv, stderr='/tmp/pwnpwn/stderr', env=env, cwd='/tmp/pwnpwn')

p.recv()

# stage 2
p.sendline(b"\x00\x0a\x00\xff")

# stage 5
r = s.remote('localhost', 56248)
r.send(b"\xde\xad\xbe\xef")

p.recv()

p.interactive()

'Pwnable.kr' 카테고리의 다른 글

Mistake  (0) 2022.03.31
Leg  (0) 2022.03.31
random  (0) 2022.03.30
passcode  (0) 2022.03.30
flag  (0) 2022.03.30