White Security
Input 문제풀이 본문
Mom? how can I pass my input to a computer program? ssh input2@pwnable.kr -p2222 (pw:guest)
Toddler's Bottle 7번 문제.
설명대로 입출력 제어와 관련된 문제입니다.
#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개의 스테이지로 나누어져
조건을 만족하지 못하면
system 문에 도달하지 못하고
즉시 return 하도록 되어 있습니다..
import subprocess arg = [ str(i) for i in range(1,100) ] arg[ord('A') - 1] = "\x00" arg[ord('B') - 1] = "\x20\x0a\x0d" proc = subprocess.Popen(['./input'] + arg, stdout = subprocess.PIPE) print(proc.pid, proc.wait()) print(proc.communicate())
파이썬의 subprocess 모듈로
시도해 보았지만 실패했습니다.
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python2.7/subprocess.py", line 711, in __init__ errread, errwrite) File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child raise child_exception TypeError: execv() arg 2 must contain only strings
subprocess 의 Popen 내부에서 사용되는
execv() 에 null 바이트를 넣으면 오류를 뿜기 때문에
다른 방법을 써야만 했습니다.
class pwnlib.tubes.process.process(argv=None, shell=False, executable=None, cwd=None, env=None, stdin=-1, stdout=<pwnlib.tubes.process.PTY object>, stderr=-2, close_fds=True, preexec_fn=<function <lambda>>, raw=True, aslr=None, setuid=None, where='local', display=None, alarm=None, *args, **kwargs)
탐색결과 pwntools 의 process 모듈을 발견했습니다.
위 모듈은 널 바이트가 포함된 인자도 정상적으로 받아들입니다.
#!/usr/bin/env python from pwn import * argv = [str(i) for i in range(100)] argv[ord('A')] = '\x00' argv[ord('B')] = '\x20\x0a\x0d' argv[ord('C')] = '9999' env = {'\xde\xad\xbe\xef' : '\xca\xfe\xba\xbe'} with open('\x0a', 'wb') as f: f.write('\x00\x00\x00\x00') with open('stderr', 'wb') as f: f.write('\x00\x0a\x02\xff') proc = process(executable = '/home/input2/input', argv = argv, stderr = open('stderr'), env = env) proc.stdin.write('\x00\x0a\x00\xff') with remote('localhost', argv[ord('C')]) as sd: sd.send('\xde\xad\xbe\xef') proc.interactive()
pwntools 를 이용하여 작성한 파이썬 스크립트입니다,
argv 변수에 argv 인자를
env 변수에 환경번수를 저장하여 전달하였고
pwntools 의 remote 모듈을 이용해 데이터를 전달했습니다.
그러던 중, process 에서 전달하는 인자를 수정해
argv[0] 을 임의대로 변경 가능하다는 것을 깨달았습니다.
argv[0] 은 실행 파일 경로로 정해져 있다고 생각했는데
절대적인 정의는 아니었나 봅니다.
with open('stderr', 'wb') as f: f.write('\x00\x0a\x02\xff') proc = process(executable = '/home/input2/input', argv = argv, stderr = open('stderr'), env = env)
소스를 확인해보면 stage 2에서
stderr 로 전달해야할 데이터를 파일로 저장한 다음
open 을 통해 파일 디스크립터를 별도로 열어
인자로 전달하는 것을 확인할 수 있는데요.
proc.stderr.write('\x00\x0a\x02\xff')
본래는 위처럼 다이렉트로 전달하려고 했습니다.
>>> proc.stderr <open file '<fdopen>', mode 'r+' at 0x7f96357c5b70> >>> proc.stderr.write('data') >>> proc.stderr.read() Traceback (most recent call last): File "<stdin>", line 1, in <module> IOError: [Errno 11] Resource temporarily unavailable
그런데 접근 모드가 r+ 인 탓인지
stderr 에 직접적인 입력은 불가능 했습니다.
input2@ubuntu:/tmp/posix$ ln -s /home/input2/flag flag
홈 폴더에는 파일을 저장할 권한이 없으므로
저는 /tmp 내에 별도로 디렉토리를 만들어
스크립트를 저장해 주었습니다.
또한 system 문의 flag 가 상대경로로 되어 있기에
심볼링 링크를 만들어 연결해 주었습니다.
input2@ubuntu:/tmp/posix$ ls ? flag solve.py stderr input2@ubuntu:/tmp/posix$ ls -b \n flag solve.py stderr
스크립트를 통해 \x0a 라는 이름으로 파일을 생성하면
파일명이 정상적으로 표시되지 않는데
ls -b 옵션을 통해 확인할 수 있습니다.
input2@ubuntu:/tmp/posix$ ./solve.py [+] Starting local process '/home/input2/input': Done [+] Opening connection to localhost on port 9999: Done [*] Closed connection to localhost port 9999 [*] Switching to interactive mode 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 :) [*] Process '/home/input2/input' stopped with exit code 0 [*] Got EOF while reading in interactive
플래그 취득에 성공했습니다.
'Wargame Writeups > pwnable.kr' 카테고리의 다른 글
Mistake 문제풀이 (0) | 2019.02.08 |
---|---|
Leg 문제풀이 (0) | 2019.02.08 |
Random 문제풀이 (0) | 2019.02.06 |
Passcode 문제풀이 (0) | 2019.02.06 |
Flag 문제풀이 (0) | 2019.02.05 |