[pwnable.kr] input(4 pts) :: Write-Up
두비니
·2020. 2. 25. 04:47
Mom? how can I pass my input to a computer program?
ssh input2@pwnable.kr -p2222 (pw:guest)
input2@pwnable:~$ cat input.c
#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;
}
아따 너무 길어브러
1~4단계까지 나눠서 분석해볼게요.
1) 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");
우선 argc는 전달된 인자의 개수, 그리고 argv는 전달되는 인자의 내용이기 때문에 조건은
1. 인자 100개전달
2. argv['A'] = '\x00'
3. argv['B'] = '\x20\x0a\x0d'
이 3개네요.
2) 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");
이건 그냥 read함수가 뭔지만 알면되죠?
첫번째는 0, 즉 표준 입력(stdin)으로 \x00\x0a\x00\xff를 넘겨주면 되는데
두번째는 2, 즉 표준 에러방식(stderr)으로 입력해줘야하는데...
이는 표준에러에 맞춰 파일 디스크립터를 생성한 뒤, process시에 stderr로 넘겨줄 수 있다고하네요.
문제느낌이 첫번째문제 fd랑 비슷하네요.
1단계보고는 그냥 길게 페이로드를 짤 생각이였는데, 이걸 보고 아예 폰툴을 써서 해야겠다 싶었습니당..
3) 3단계
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
일단 getenv함수는 환경변수를 불러오는 함수입니다. (환경변수가 뭔지 모르겠다면 구글링ㄱㄱ)
lob에서 환경변수를 꽤 다뤘었죠.
저 if문을 건너뛰려면 \xde\xad\xbe\xef라는 환경변수의 내용이 \xca\xfe\xbe\xbe가 되어야하네요.
4) 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");
파일 여는 것에 대한 문제네요. \x0a라는 파일을 열고, 처음 4바이트가 \x00\x00\x00\x00이여야하네요!
5) 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");
코드는 긴데 결국 요약하면 argv['C']에 해당하는 포트를 열고, 그 포트로 \xde\xad\xbe\xef가 전송되어야 한다는 내용이네요.
그래서 페이로드를 짜보면
from pwn import *
#stage 1
argvs = ["" for i in range(100)]
argvs[0] = "/home/input2/input"
argvs[ord('A')] = "\x00"
argvs[ord('B')] = "\x20\x0a\x0d"
#stage5 일부
argvs[ord('C')] = "66666"
#stage2
stderrfd = open('./stderr', 'w+')
stderrfd.write('\x00\x0a\x02\xff')
stderrfd.seek(0)
#stage3
with open('./\x0a', 'w') as fd:
fd.write('\x00\x00\x00\x00')
#인자전달, stderr파일 열기, 황경변수 설정 등
r = process(executable='/home/input2/input',argv=argvs, stderr=stderrfd, env={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'})
r.recvuntil('Stage 1 clear!\n')
#stage2의 stdin
r.send("\x00\x0a\x00\xff")
r.recvuntil('Stage 2 clear!\n')
r.recvuntil('Stage 3 clear!\n')
r.recvuntil('Stage 4 clear!\n')
p=remote('127.0.0.1', 66666)
p.send('\xde\xad\xbe\xef')
p.close()
r.interactive()
이렇게 되겠네요. pwntools 이용했습니다.
ln -s /home/input2/flag flag
참 이렇게 심볼릭 링크를 걸어놓아야 내가 만들어놓은 tmp파일의 exploit파일이 실행될 수 있으니 꼭 하십셔.
굳
'War Games > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] mistake(1 pts) :: Write-Up (0) | 2020.07.07 |
---|---|
[pwnable.kr] leg(2 pts) :: Write-Up (0) | 2020.07.06 |
[pwnable.kr] random(1 pt) :: Write-Up (0) | 2020.02.24 |
[pwnable.kr] passcode(10 pts) :: Write-Up (0) | 2020.01.28 |
[pwnable.kr] flag (7 pts) :: Write-Up (0) | 2019.11.10 |