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 stringssubprocess 의 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 |