cgy12306
[pwnable.kr] input 본문
[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;
}
각각의 스테이지가 있고 해당 스테이지 클리어를 하면 다음 스테이지로 넘어간다.
스테이지 5까지 클리어하면 /bin/cat flag 명령어를 실행한다.
각각 스테이지 별로 끊어서 차근차근 해야 한다.
우선 첫번째 스테이지이다.
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' 번째 인자가 '\x00', 'B' 번째 인자가 '\x20\x0a\x0d'이어야 한다.
A는 16진수로 변환하면 0x41, B는 0x42이다. 즉, 0x41번째 인자와 0x42번째 인자를 맞춰줘야 한다.
from pwn import *
argvs = ["" for i in range(0,100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
p = process(executable = '/home/input2/input', argv = argvs)
p.interactive()
첫번째 스테이지 통과 코드이다.
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");
두번째 스테이지이다.
buf를 4byte 입력받고, '\x00\x0a\x00\xff'와 같아야 한다. 또 파일 디스크립터가 2로 주어지고 '\x00\x0a\x02\xff'와 같아야한다.
파일 디스크립터 값인 2는 표준 에러이다.
표준에러에서 출력된 값을 buf에 4byte 만큼 넣어주는데 그 값이 '\x00\x0a\x02\xff'이어야 한다.
from pwn import *
argvs = ["" for i in range(0,100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
p = process(executable = '/home/input2/input', argv = argvs, stderr = open('./err'))
payload = '\x00\x0a\x00\xff'
with open('./err', 'w') as f:
f.write('\x00\x0a\x02\xff')
p.sendline(payload)
p.interactive()
err 파일을 임시로 생성한 후 표준 에러를 err 파일로 출력하게 해놓고, 그 안에 '\x00\x0a\x02\xff'를 넣어준다.
첫번째 read() 함수 호출 될때도 값을 입력해야 하므로 '\x00\x0a\x00\xff'를 보내준다.
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
세번째 스테이지이다.
'\xca\xfe\xba\xbe'와 getenv로 해당 환경변수 값을 비교해서 같아야 한다.
from pwn import *
argvs = ["" for i in range(0,100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
p = process(executable = '/home/input2/input', argv = argvs, stderr = open('./err'), env={'\xde\xad\xbe\xef' : '\xca\xfe\xba\xbe'})
payload = '\x00\x0a\x00\xff'
with open('./err', 'w') as f:
f.write('\x00\x0a\x02\xff')
p.sendline(payload)
p.interactive()
네번째 스테이지 코드이다.
// 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' 파일에서 4바이트를 1번 읽어와서 '\x00\x00\x00\x00'과 비교해서 같으면 스테이지를 통과하는 코드이다.
그럼 해당 파일을 생성하고, 안에 값을 입력하는 코드를 작성하자.
from pwn import *
argvs = ["" for i in range(0,100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
p = process(executable = '/home/input2/input', argv = argvs, stderr = open('./err'), env={'\xde\xad\xbe\xef' : '\xca\xfe\xba\xbe'})
payload = '\x00\x0a\x00\xff'
with open('./err', 'w') as f:
f.write('\x00\x0a\x02\xff')
with open('\x0a', 'w') as f:
f.write('\x00\x00\x00\x00')
p.sendline(payload)
p.interactive()
마지막 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");
'C'번째 인자를 port 번호로 삼는다.
해당 포트를 열고, 받은 값을 buf에 저장한다. 반환값은 수신한 데이터의 길이를 반환한다.
우리는 열려있는 포트로 '\xde\xad\xbe\xef' 값을 보내주면 된다.
from pwn import *
argvs = ["" for i in range(0,100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
argvs[ord('C')] = '8080'
p = process(executable = '/home/input2/input', argv = argvs, stderr = open('./err'), env={"\xde\xad\xbe\xef": "\xca\xfe\xba\xbe"})
payload = '\x00\x0a\x00\xff'
with open('./err', 'w') as f:
f.write('\x00\x0a\x02\xff')
with open('\x0a', 'w') as f:
f.write('\x00\x00\x00\x00')
p.sendline(payload)
r = remote('localhost', 8080)
r.send('\xde\xad\xbe\xef')
p.interactive()
스테이지는 전부 클리어 됐지만 flag를 보여주지 않는다. 이유는 현재 디렉토리에는 flag 파일이 없기 때문이다.
현재 작업하고 있는 디렉토리에 flag 파일을 원래 flag 파일에 심볼릭 링크를 걸어 확인하면 된다.
'Wargame > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] blackjack (0) | 2020.01.27 |
---|---|
[pwnable.kr ] coin1 (0) | 2020.01.26 |
[pwnable.kr] random (0) | 2020.01.26 |
[pwnable.kr] passcode (0) | 2020.01.26 |
[pwnable.kr] shellshock (0) | 2020.01.26 |
Comments