-
[pwnable.kr][Toddler's Bottle] 7. inputWargame/pwnable.kr 2019. 7. 26. 22:27
ID: input2, PW: guest로 접속한 후 파일을 확인해보니 input 파일이 있었다.
input.c 파일을 확인해보았다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667#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");// argvif(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");// stdiochar 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");// envif(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;printf("Stage 3 clear!\n");// fileFILE* 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");// networkint 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 flagsystem("/bin/cat flag");return 0;}확인해보면 엄청 긴 코드가 있다. 5개의 stage를 만족하면 flag가 출력된다고 한다. 각 stage별로 코드를 확인해보자.
stage 1. argv
argc는 인자의 개수, argv는 각 인자의 문자열을 말한다.
stage 1을 통과하기 위해서는,
1) 인자의 개수 = 100
2) argv['A'] == \x00 : 65번째 인자 = \x00
3) argv['B'] == \x20\x0a\x0d : 66번째 인자 = \x20\x0a\x0d
이 세 가지 조건을 만족해야 한다.
stage 2. stdio
stage 2는 fd(file descriptor)와 관련된 조건이다.
stage 2를 통과하기 위해서는,
1) 표준 입력(stdin) = \x00\x0a\x00\xff
2) 표준 오류(stderr) = \x00\x0a\x02\xff
를 만족해야 한다.
stage 3. env
stage 3는 환경변수와 관련된 스테이지이다.
\xde\xad\xbe\xef라는 환경변수에 \xca\xfe\xba\xbe를 넣어주면 stage 3을 통과할 수 있다.
stage 4. file
stage 4에서는 \x0a라는 파일을 읽기 전용으로 open한다. 그 다음, open한 파일의 첫 4 bytes가 \x00\x00\x00\x00이면 stage 4를 클리어할 수 있다.
stage 5. network
stage 5는 소켓에 관련된 스테이지이다.
12345678910111213141516171819202122232425// networkint 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; //IPv4saddr.sin_addr.s_addr = INADDR_ANY; //32bit IPv4 주소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; //소켓으로부터 수신한 바이트 수 == 4 bytesif(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0; //버퍼 == \xde\xad\xbe\xefprintf("Stage 5 clear!\n");atoi(argv['C']), 즉 67번째 인자를 정수형으로 변환한 값이 포트 번호가 된다. 해당 포트 번호로 서버 소켓을 연 다음\xde\xad\xbe\xef를 전송하면 stage 5가 클리어될 것이다.
이제 flag를 얻기 위한 모든 조건을 알아냈다. 그런데 이 조건을 만족하는 인자를 한번에 전달하려면 따로 익스플로잇 코드를 작성해야 해서 다른 분들의 코드를 참고했다...
코드 작성은 /home/input2 디렉토리에서 불가능하므로 tmp 디렉토리에 새 디렉토리를 생성한 후 그 안에서 코드를 작성해야 한다.
작성한 코드는 아래와 같다.
123456789101112131415161718192021222324252627282930313233from pwn import *#stage 1argvs = [str(i) for i in range(100)] #인자 100개 채움argvs[ord('A')] = '\x00' #65번째 인자argvs[ord('B')] = '\x20\x0a\x0d' #66번째 인자#stage 2: 표준오류 조건with open('./stderr', 'a') as f:f.write('\x00\x0a\x02\xff')#stage 3: 환경변수 설정env_var = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}#stage 4: \x0a 파일 생성with open('./\x0a', 'a') as f:f.write('\x00\x00\x00\x00')#stage 5: 포트번호 설정argvs[ord('C')] = '40000'#인자, 환경변수, 실행할 파일 설정target = process(executable='/home/input2/input', argv=argvs, stderr=open('./stderr'), env=env_var)#stage 2: 표준입력 조건target.sendline('\x00\x0a\x00\xff')#stage 5: 열린 소켓을 통해 데이터 전송conn = remote('localhost', 40000)conn.send('\xde\xad\xbe\xef')conn.close()target.interactive()코드를 다 작성하면 작성한 프로그램을 실행한다. 그런데, 파일을 실행해도 flag가 보이지 않는다.
현재 디렉토리에 flag 파일이 없기 때문에 작성한 익스플로잇을 실행해도 flag가 나타나지 않는 것이다. 그래서 flag 파일에 대해 심볼릭 링크를 걸어준 다음에 다시 파일을 실행하니 flag가 나타났다.
[참고 자료]
socket( ) 소켓 생성: http://forum.falinux.com/zbxe/index.php?document_srl=429387&mid=C_LIB
recv( ) 소켓으로부터 자료 수신: http://forum.falinux.com/zbxe/index.php?document_srl=441107&mid=C_LIB
pwntools 사용법: https://lclang.tistory.com/90?category=254640, https://lclang.tistory.com/82?category=254640
익스플로잇 코드: https://gmltnscv.tistory.com/27, https://mandu-mandu.tistory.com/76
'Wargame > pwnable.kr' 카테고리의 다른 글
[pwnable.kr][Toddler's Bottle] 9. mistake (0) 2019.08.04 [pwnable.kr] [Toddler's Bottle] 8. leg (0) 2019.07.31 [pwnable.kr][Toddler's Bottle] 6. random (0) 2019.07.26 [pwnable.kr][Toddler's Bottle] 5. passcode (0) 2019.07.19 [pwnable.kr][Toddler's Bottle] 4. flag (0) 2019.07.18