서브웨이

pwnable.kr input 풀이 본문

Pwnable/pwnable.kr

pwnable.kr input 풀이

샌드위치메이커 2020. 8. 11. 17:43

자 일단 설명이 간단합니다.

일단 세개의 파일이 있습니다. 먼저 소스코드를 봅시다.

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

대충 보아하니 여러가지 입력 방법에 대해 익히는 문제인듯 합니다. 주어진 스테이지를 모두 통과하면 플래그를 볼 수 있네요.

참고로 문제 디렉토리에선 파일 생성이 불가능하니 /tmp 디렉토리에 아무 디렉토리나 만들어서 거기서 작업합니다.

 

자 일단 스테이지1을 봅시다.

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여야 하네요.

간단하게 pwntools를 사용합시다.

from pwn import *

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

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

 

스테이지2를 봅시다.

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인지 확인합니다.

그럼 간단하게 파일에 stderr로 넘겨줄 내용을 써서 stderr로 지정해주고, stdin에 쓸 내용을 send해주면 해결할 수 있습니다.

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

r = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"))
r.send("\x00\x0a\x00\xff")

 

스테이지3를 봅시다.

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

그냥 \xde\xad\xbe\xef라는 환경변수의 값을 \xca\xfe\xba\xbe로 지정해주면 됩니다.

envirn = {"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}
r = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"), env=envirn)

간단합니다.

 

스테이지4를 봅시다.

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

뭐 간단합니다. 그냥 \x0a라는 이름을 가진 파일에 4바이트를 0으로 채우면 됩니다.

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

 

스테이지5를 봅시다.

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;

뭐 그냥 간단한 소켓 코드입니다. 그냥 아스키코드로 C, 즉 67번째 인자에 넘겨준 값을 포트번호로 하는 로컬 서버에 4바이트를 보내서 그 값이 \xde\xad\xbe\xef인지 확인합니다.

argvs[67] = "12345"
client = remote("127.0.0.1", 12345)
client.send("\xde\xad\xbe\xef")

끝~~~!~!!~!~~!

하지만 아직 플래그를 열 수 없습니다. 해당 폴더에 플래그가 없으므로 플래그파일의 소프트링크를 하나 생성해줍니다.

ln -s /home/input2/flag flag

이 명령어를 쓰면 됩니다.

 

전체 페이로드는 다음과 같습니다.

from pwn import *

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

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

#Stage3
envirn = {"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}

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

#Stage5
argvs[67] = "12345"

r = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"), env=envirn)
#Stage2
r.send("\x00\x0a\x00\xff")

#Stage5
client = remote("127.0.0.1", 12345)
client.send("\xde\xad\xbe\xef")

r.interactive()

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

pwnable.kr mistake 풀이  (0) 2020.08.14
pwnable.kr leg 풀이  (0) 2020.08.13
pwnable.kr random 풀이  (0) 2020.08.11
pwnable.kr passcode 풀이  (0) 2020.07.21
pwnable.kr flag 풀이  (0) 2020.02.18
Comments