본문 바로가기

Write-Up/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;
}

 

 

하나씩 분석해보자.

 

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

//argv

argc, argv에 대해 묻는걸 봐서 input값에 인자가 어떻게 들어가는지를 묻는것같다.

argc개수가 100개가 맞는지 확인한다.

argc개수를 100개 맞춰주고

argv['A'] = argv[65] = \x00

argv['B'] = argv[66] = \x20\x0a\x0d

stage 1이 풀리게된다.

from pwn import *

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


p = process(executable="/home/input2/input", argv=argvs)
p.interactive()

 

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

// stdio

fd풀때 배운 파일디스크립터를 묻는것 같다.

복습하자면

read(0, buf, 4);

0은 표준입력이니 buf에 \x00\x0a\x00\xff(4byte) 입력을 줘야한다.

fd 2는 stderr(표준 에러)이다.

stderr로 \x00\x0a\x02\xff을 주어야한다.

위 두개 조건이 만족되면 clea가 되는데

여기서 stderr을 어떻게 줘야하는지 고민을 많이했다.

구글링해보니

파일을 생성에서 넣어주고 그 파일로 대체하면 된다하더라.

from pwn import *

#stage 1
argvs = ["" 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')
     
pay = '\x00\x0a\x00\xff'
p = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"))
p.sendline(pay)
p.interactive()

stage 3

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

//env

환경변수를 이용하는 stage다.

if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef")))

\xde\xad\xbe\xef의 환경변수에 \xca\xfe\xba\xbe를 넣어주면 된다.

from pwn import *

argvs = ["" 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')
pay = '\x00\x0a\x00\xff'

#stage 3
env2 = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
p = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"),env=env2)
p.sendline(pay)
p.interactive()

분명 stage 3까지 풀었는데 결과를 확인해보면 stage 4까지 clear되어있다.

 

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

이미 stage 3을 풀면서 clear했지만 분석해보자.

\x0a파일을 열고

\x00\x00\x00\x00를 써주면 된다.

어려운 부분은 아닌것 같다.

from pwn import *

#stage 1
argvs = ["" 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')
pay = '\x00\x0a\x00\xff'

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

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

p = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"),env=env2)
p.sendline(pay)
p.interactive()

 

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

이부분을 분석하기위해서는 socket에대한 사전 지식이 필요하다.

atoi(argv['C']) = argv['C'] 의 정수형으로 변환한 값을 포트번호로 소켓서버를 연결하고

(atoi = char to int = 문자열을 정수 타입으로 반환하는 함수)

해당 포트번호로 0xdeadbeef 보내주면된다.

from pwn import *

#stage 1
argvs = ["" 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')
pay = '\x00\x0a\x00\xff'

#stage 3
env2 = {'\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] = '2020'

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

sleep(3)

r = remote('localhost', 2020)
r.send('\xde\xad\xbe\xef')

p.interactive()

flag가 tmp에는 존재하지 않으므로 심볼릭 링크를 걸어서 실행시켜주면

ln -s /home/input2/flag flag

 

Mommy! I learned how to pass various input in Linux :)

'Write-Up > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] mistake 풀이  (0) 2021.09.23
[pwnable.kr] random 풀이  (0) 2021.07.26
[pwnable.kr] passcode 풀이  (0) 2021.07.22
[pwnable.kr] blukat 풀이  (0) 2021.03.26
[pwnable.kr] memcpy 풀이  (0) 2021.03.25