#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;
}
코드를 보면 다양한 값들을 다양한 인자로 프로그램에 전달해야한다.
이 문제를 풀면서 pwntools의 기능에 대해 자세히 알게 된거같다.
인자로 줄수있는게 이렇게 많았다니;
from pwn import *
context.log_level = 'debug'
argv = [str(i) for i in range(100)]
argv[65] = "\x00"
argv[66] = "\x20\x0a\x0d"
argv[67] = "12600"
with open('./a', 'a') as f:
f.write('\x00\x0a\x02\xff')
with open('\x0a', 'a') as f1:
f1.write('\x00\x00\x00\x00')
p = process(executable='/home/input2/input', argv=argv, stderr=open('./a'), env={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'})
p.send('\x00\x0a\x00\xff')
sleep(1)
r = remote('127.0.0.1', 12600)
r.sendline('\xde\xad\xbe\xef')
p.interactive()
일단 코드부터...
문제 파일이 있는 경로엔 파일 생성 권한이 없기때문에 /tmp폴더에 익스코드를 짜자.
총 5가지의 검사를 통과해야 플래그를 뱉어준다.
첫번째는 인자갯수와 인자의 값을 검사한다.
argv는 process에 리스트 형태로 넣어줘야한다.
그래서 argc를 100개 맞춰주기위해 반복문을 돌리고 우리가 원하는 위치의 argv를 원하는 값으로 바꿔준다.
두번째는 stdin, stderr값을 검사한다.
stdin은 간단하게 send로 해결가능하지만,
stderr는 파일형식으로 넣어줘야 한다고 한다.
a라는 파일에 우리가 원하는 값을 저장하고 process에 파일형식으로 넣어준다.
세번째는 환경변수를 검사한다.
process에 딕셔너리 형식으로 원하는 값의 환경변수를 넣으면 pwntools가 알아서 잘 실행해준다.
네번째는 파일의 값을 검사한다.
\x0a라는 파일을 검사하는데
커맨드라인 환경에선 잘 안만들어지니 pwntools를 쓰자
다섯번째는 argv값으로 소켓을 열어 값을 받고 그값을 검사한다.
argv에 우리가 원하는 포트를 넣고 remote로 연결해 값을 넣어주자.
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 :)
[*] Got EOF while reading in interactive
$
flag = Mommy! I learned how to pass various input in Linux :)
상당히 귀찮은 문제였다.