엄마~!~!어떻게 하면 프로그램에 내 입력이 통과할 수 있는거야? 라고 물어본다. 살짝 리버싱문제 같은 느낌도 든다. 일단 접속해보자.
input파일을 실행해 보자.
인자를 직접 전달해 줘야 하는 것 같다. input.c파일을 읽어보면
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #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; } | cs |
이렇게 꽤 긴 코드가 있다.
처음부터 차례대로 분석해보자.
1 2 3 4 5 | // 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"); | cs |
1단계는 argv에 관한 코드이다.
argc와 argv는 main함수의 인수인데 argc에는 인자의 개수가 저장되고 argv에는 인자가 각각 들어있다.
그리고 여기서 argv의 첫 번째 배열에는 실행 명령어가 들어가있다.
예를들어서 ./input a b와 같이 인자를 전달해준다면
argc에는 3이 들어가고 argv[0]에 ./input, argv[1]에 a, argv[2]에 b가 들어가게 되는 것이다.
이제 저 코드를 해석해보면 인자의 개수를 100개로 하고 argv['A'] 즉 argv[65]에는 \x00이 들어가고 argv['B'] 즉 argv[66]에는 \x20\x0a\x0d가 들어가게 하면 되는것이다.
이제 2단계를 확인해보자.
1 2 3 4 5 6 7 | // 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"); | cs |
2단계는 파일 디스크립터 관련된 문제이다.
0 : 표준입력
1 : 표준출력
2 : 표준에러
이것만 이용하면 바로 풀 수 있을 것이다.
read(0, buf, 4)는 표준 입력의 4바이트를 읽어와서 \x00\x0a\x00\xff와 비교하고
read(2, buf, 4)는 표준 에러의 4바이트를 읽어와서 \x00\x0a\x02\xff와 비교한다.
여기서 표준 입력은 그냥 \x00\x0a\x00\xff를 넣어주면 되는데 표준에러에 어떻게 넣어야 할까에 대해서 고민을 좀 했다.
그냥 간단하게 어떤 파일을 하나 열어서 거기에 \x00\x0a\x02\xff를 넣고 표준에러의 fd값을 저 파일로 바꿔주면 될 것 같아서 해보니까 성공했다~!
쓰기 권한이 있는 /tmp에 가서 test파일을 만들고 실행시켜보았다.
2단계까지의 페이로드는 다음과 같다.
from pwn import *
context.log_level = 'debug'
k = open("ryu", "w")
k.write("\x00\x0a\x02\xff")
#ryu라는 파일을 열어서 해당 값을 입력해준다.
k = open("ryu", "r")
argv = [str(i) for i in range(100)]
argv[ord('A')] = '\x00'
argv[ord('B')] = '\x20\x0a\x0d'
#전달할 인자를 생성해준다.
p = process(argv, stderr=k, executable='/home/input2/input')
p.recv(1000)
p.send('\x00\x0a\x00\xff')
#표준입력으로 값을 전달해준다.
p.recv(1000)
이제 3단계를 확인해보자.
1 2 3 | // env if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; printf("Stage 3 clear!\n"); | cs |
getenv함수는 해당 환경변수의 값을 가져오는 함수니까 "\xde\xad\xbe\ef"라는 이름의 환경변수 값이 "\xca\xfe\xba\xbe"이면 된다.
간단하게
e = {'\xde\xad\xbe\xef' : '\xca\xfe\xba\be'}
라는 코드를 추가하고 환경변수로 e를 넣어주면 된다.
성공이다~!~!
이제 4단계를 확인해보자.
1 2 3 4 5 6 7 | // 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"); | cs |
\x0a라는 파일을 열어서 처음 4바이트가 \x00\x00\x00\x00이면 성공이다.
이번 단계도 간단하게 파일을 열어서 저 데이터를 넣어주기만 하면 된다.
l = open("\x0a", "w")
l.write("\x00\x00\x00\x00")
l.close()
성공이다~!~!
이제 마지막 단계를 확인해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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"); | cs |
argv['C']에 있는 값을 포트번호로 소켓을 생성하고 listen하고 있다가 데이터가 들어오면 그 데이터를 buf에 저장하고 \xde\xad\xbe\xef와 비교하는 코드이다.
이번단계를 해결하려면 argv['C']에 포트번호를 넣어놓고 remote를 통해 lacalhost를 호출하고 데이터를 전달해주면 될 것 같다.
from pwn import *
context.log_level = 'debug'
k = open("ryu", "w")
k.write('\x00\x0a\x02\xff')
#ryu라는 파일을 열어서 해당 값을 입력해준다.
l = open("\x0a", "w")
l.write("\x00\x00\x00\x00")
l.close()
#4단계 파일에 값을 넣어준다.
k = open("ryu", "r")
argv = [str(i) for i in range(100)]
argv[ord('A')] = '\x00'
argv[ord('B')] = '\x20\x0a\x0d'
argv[ord('C')] = '1010'
#전달할 인자를 생성해준다.
e = {'\xde\xad\xbe\xef' :'\xca\xfe\xba\xbe'}
#환경변수를 만들어준다.
p = process(argv, stderr=k, env=e, executable='/home/input2/input')
p.recv(1000)
p.send('\x00\x0a\x00\xff')
#표준입력으로 값을 전달해준다.
p.recv(1000)
sleep(3)
#소켓 열리는 시간도 생각해준다.
m = remote('lacalhost', 1010)
m.send('\xde\xad\xbe\xef')
p.recv(1000)
p.recv(1000)
성공은 했는데 나오지가 플래그가 뜨지 않았다ㅠㅠ
이 파일이 실행될 때의 경로가 /tmp안이니까 플래그 파일이 해당경로 안에 없어서 그런 것이였다.
중간에 파일을 만들기도 해야하니까 옮기는건 힘들고 심볼릭 링크를 걸어줬다.
이런식으로 해주면
플래그가 나온 것을 확인할 수 있다~!~!
'pwnable > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] mistake (0) | 2019.02.24 |
---|---|
[pwnable.kr] leg (0) | 2019.02.20 |
[pwnable.kr] random (0) | 2019.01.15 |
[pwnable.kr] passcode (0) | 2019.01.13 |
[pwnable.kr] flag (0) | 2019.01.12 |