일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 네트워크
- Buffer Overflow
- OS
- 해킹
- System
- Spring
- pwnable
- 정보보안기사
- 웹해킹
- 워게임
- stack overflow
- Payload
- webhacking
- Spring Framework
- 운영체제
- Shell code
- hacking
- 정보처리기사 실기
- 정보보안기사 실기
- Spring MVC
- webhacking.kr
- LOB
- BOF
- Pwnable.kr
- wargame
- SQL
- Lord of BOF
- PWN
- system hacking
- Operating System
- Today
- Total
DongDD's IT
[pwnable.kr] input 본문
[pwnable.kr] input
Problem
내가 넣은 input으로 컴퓨터 프로그램을 어떻게 통과할 수 있는지 묻고 있었다.
먼저 ssh를 통해 문제에 접속해보았다.
이번 문제에서도 flag 파일과 input 실행 파일, 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 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 |
이번 문제의 소스 코드는 앞 문제들과 달리 꽤 길게 되어있었다.
각 Stage를 클리어할 수 있는 조건으로 코딩되어있었고 stage 5까지 클리어하게 되면 cat flag를 통해 flag를 알려주는 형태로 되어있었다.
Solution
각 stage 별로 나누어져있기 때문에 한번에 모든 값으 넣어주기 어려울 것 같아 pwntool을 사용하기 위해 로컬에서 ssh를 접속하는 방식으로 python을 작성해야겠다는 생각이 들었다.
Stage1
1 2 3 4 5 | 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 |
Stage1는 argv를 이용한 문제였다.
프로그램 실행 시 인자가 총 100개여야 하고 argv['A'], 65번째 인자가 "\x00"이여야 하고 argv['B'] , 66번째 이자가 "\x20\x0a\x0d"여야 stage1을 클리어할 수 있었다.
ssh 접속 후 pwntool에 있는 run을 이용해 인자를 string으로 저장해 넣었었는데 아스키코드로 인식해서 그런지 통과할 수가 없었다. 그래서 list형식으로 인자를 만들어주고 process를 이용해 실행하니 통과할 수 있었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" p2 = p.process(executable='/home/input2/input', argv=arg) print p2.recvuntil("clear!\n") | cs |
arg라는 100개의 list를 만들어주고 arg[65] = "\x00", arg[66] = "\x20\x0a\x0d"를 넣어주고 process를 통해 실행 파일과 인자를 넣어주니 첫 번째 stage를 통과할 수 있었다.
Stage2
1 2 3 4 5 6 7 | 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 |
두번째 stage는 stdin과 stderr를 이용하는 문제였다.
먼저 stdin의 4bytes를 읽어와서 이것이 "\x00\x0a\x00\xff"인지 확인하고 이 값과 일치해야 한다.
그 후에 stderr의 4bytes를 읽어와서 이것이 "\x00\x0a\x02\xff"인지 확인하고 이 값과 일치해야 Stage2를 clear할 수 있게 되어있었다.
stdin은 그냥 send를 통해 보낼 수 있지만 stderr를 따로 파일을 생성해 설정해주어야 했다.
err라는 파일을 생성해야하는데 input2에서는 파일 생성이 불가능했기 때문에 /tmp에 폴더를 만들어 그 폴더에 err라는 파일을 넣어주고 stderr를 그 파일로 대체했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" p.run("mkdir /tmp/dongdd") p.write('/tmp/dongdd/err', "\x00\x0a\x02\xff") p2 = p.process(executable='/home/input2/input', argv=arg,stderr="/tmp/dongdd/err") print p2.recvuntil("clear!\n") p2.send("\x00\x0a\x00\xff") print p2.recvuntil("clear!\n") | cs |
run을 통해 /tmp에 원하는 폴더를 생성해주고 write를 통해 그 폴더에 파일을 생성해 값을 넣어주었다.
그리고 process 인자에 stderr를 넣어줘 stderr를 생성한 파일로 대체하고 send를 통해 stdin으로 값을 넣어주었다.
Stage3
1 2 3 4 | // env if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; printf("Stage 3 clear!\n"); | cs |
세번째 stage는 환경변수를 이용하는 문제였다.
환경변수 "\xde\xad\xe\xef"의 값이 "\xa\xfe\xba\be"와 같으면 clear를 할 수 있게 되어있었다.
process에 환경 변수를 넣어줄 수 있었고 환경 변수를 넣을 때는 dictonary 자료형을 사용해야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" p.run("mkdir /tmp/dongdd") p.write('/tmp/dongdd/err', "\x00\x0a\x02\xff") en = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"} p2 = p.process(executable='/home/input2/input', argv=arg,stderr="/tmp/dongdd/err",env=en) print p2.recvuntil("clear!\n") p2.send("\x00\x0a\x00\xff") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") | cs |
먼저 dictionary 자료형으로 "\xde\xad\xbe\xef"라는 변수에 "\xca\xfe\xba\xbe"를 넣어주었다.
그리고 이 변수를 process의 환경 변수 인자로 env에 넣어주어 실행했다.
Stage4
1 2 3 4 5 6 7 8 | // 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 |
네번째 stage는 file descriptor를 이용한 문제였다.
"\x0a"라는 파일에서 4bytes를 읽어 이 값이 "\x00\x00\x00\x00"이면 통과할 수 있게 되어있었다.
아까 err파일을 생성했던 방식과 마찬가지로 "\x0a"라는 파일을 생성하고 "\x00\x00\x00\x00"을 넣어주면 클리어할 수 있다.
하지만 여기서는 절대 경로로 표시된 것이 아니기 때문에 파일을 생성할 수 없는 input2 폴더에서는 작업할 수 없다.
그렇기 때문에 현재 디렉토리를 /tmp/dongdd로 바꿔주었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" p.run("mkdir /tmp/dongdd") p.write('/tmp/dongdd/err', "\x00\x0a\x02\xff") p.write('/tmp/dongdd/\x0a', "\x00\x00\x00\x00") en = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"} p2 = p.process(cwd="/tmp/dongdd/",executable='/home/input2/input', argv=arg,stderr="/tmp/dongdd/err",env=en) print p2.recvuntil("clear!\n") p2.send("\x00\x0a\x00\xff") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") | cs |
process 인자에 현재 디렉토리(cwd)를 "/tmp"의 생성한 폴더로 바꿔주고 write를 통해 "\x0a"파일을 생성해 "\x00\x00\x00\x00"을 넣어주었다.
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 | // 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"); | cs |
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 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" arg[67] = "12345" p.run("mkdir /tmp/dongdd") p.write('/tmp/dongdd/err', "\x00\x0a\x02\xff") p.write('/tmp/dongdd/\x0a', "\x00\x00\x00\x00") en = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"} p2 = p.process(cwd="/tmp/dongdd/",executable='/home/input2/input', argv=arg,stderr="/tmp/dongdd/err",env=en) print p2.recvuntil("clear!\n") p2.send("\x00\x0a\x00\xff") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") p1 = p.remote('localhost',12345) p1.send("\xde\xad\xbe\xef") print p2.recv() | cs |
먼저 arg[67]에 사용할 포트 번호(12345)를 넣어주었다. 그 후에 process를 통해 실행되면서 accept 부분에 정지해있을 것이기 때문에 remote(nc)를 이용해 그 포트로 접속해준 후 "\xde\xad\xbe\xef"를 보내주게 되면 clear할 수 있다.
Stage5를 클리어했지만 flag가 출력되지 않았다. stage3을 클리어하기 위해 cwd를 바꿔줬기 때문에 flag가 없어서 인 것 같았다.
flag를 input2 폴더에 있지만 현재 디렉토리는 /tmp/dongdd이기 때문에 이 폴더에 심볼릭 링크를 이용해 flag파일을 생성해주면 문제를 통과할 수 있을 것 같았다.
코드에 심볼릭 링크를 실행하는 한 줄을 추가했고 flag를 획득할 수 있었다.
************** Answer & Flag **************
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 | from pwn import * p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") arg = [str(i) for i in range(100)] arg[65] = "\x00" arg[66] = "\x20\x0a\x0d" arg[67] = "12345" p.run("mkdir /tmp/dongdd") p.write('/tmp/dongdd/err', "\x00\x0a\x02\xff") p.write('/tmp/dongdd/\x0a', "\x00\x00\x00\x00") en = {'\xde\xad\xbe\xef' : "\xca\xfe\xba\xbe"} ar = [str(i) for i in range(4)] ar[1] = "-s" ar[2] = "/home/input2/flag" ar[3] = "flag" p.process(cwd='/tmp/dongdd/', executable='/bin/ln',argv=ar) p2 = p.process(cwd="/tmp/dongdd/",executable='/home/input2/input', argv=arg,stderr="/tmp/dongdd/err",env=en) print p2.recvuntil("clear!\n") p2.send("\x00\x0a\x00\xff") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") print p2.recvuntil("clear!\n") p1 = p.remote('localhost',12345) p1.send("\xde\xad\xbe\xef") print p2.recv() | cs |
ln을 실행하기 위해 ar list 변수를 생성해 인자를 넣어주었고 process를 통해 실행히켜 flag를 "/tmp/dongdd"에 생성했다.
계속 실행하게 되면 생성되는 파일들이 엉킬 수가 있기 때문에 실행하기전에 "/tmp"에 들어가 생성했던 파일들을 삭제해주어야 한다.
코드에 넣을 수 있었지만 그냥 수동으로 지우는 방법을 택했다.
Flag : Mommy! I learned how to pass various input in Linux :)
'Wargame > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] mistake (0) | 2018.06.14 |
---|---|
[pwnable.kr] leg (0) | 2018.06.10 |
[pwnable.kr] random (0) | 2018.04.02 |
[pwnable.kr]passcode (0) | 2018.03.28 |
[pwnable.kr] flag (0) | 2018.03.09 |