Self-Improvement
[pwnable.kr] input 풀이 (pwntools process executable!!) 본문
소스코드
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 |
Stage 1
argv[0]을 포함하여 인자는 100개가 되어야 한다.
argv['A'] = argv[65]는 "\x00"이 되어야 한다.
argv['B'] = argv[66]는 "\x20\x0a\x0d"가 되어야 한다.
1
2
3
4
5
|
// 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");
|
cs |
`python ~~`으로 하면 \x00을 널 바이트로 인식하여 안되기 때문에 Pythown pwntools을 이용해서 풀어본다.
1
2
3
4
5
6
7
8
9
|
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!')
|
cs |
Stage 2
read() 함수로 입력값이 "\x00\x0a\x00\xff"이어야 하며 표준 에러가 "\x00\x0a\x02\xff"이어야 한다.
이때 표준에러는 pwntools의 process에서 설정해줄 수 있다.
표준에러를 설정할 시엔 파일형식으로만 가능하다.
그냥 \x00\x0a\x00\xff, \x00\x0a\x02\xff을 p.send() 형식으로 보내도 클리어가 되어진다.
1
2
3
4
5
6
7
|
// 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");
|
cs |
1
2
3
4
5
6
7
8
|
p.run("mkdir /tmp/johg")
p.write('/tmp/johg/err', "\x00\x0a\x02\xff")
p2 = p.process(executable='/home/input2/input', argv=arg, stderr="/tmp/johg/err")
payload="\x00\x0a\x00\xff"
p2.send(payload)
print p2.recvuntil('clear!')
|
cs |
Stage 3
getenv() 함수로 "\xde\xad\xbe\xef"의 값을 가져와 "\xca\xfe\xba\xbe"와 동일한지를 확인한다.
여기서도 pwntools의 process에서 설정이 가능하다.
1
2
3
|
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\
|
cs |
1
2
3
|
en={'\xde\xad\xbe\xef':"\xca\xfe\xba\xbe"}
p2 = p.process(executable='/home/input2/input', argv=arg, stderr="/tmp/johg/err", env=en)
|
cs |
Stage 4
\x0a 파일을 내용을 buf로 읽어들여 "\x00\x00\x00\x00"과 동일한지 확인하고 있다.
리눅스 명령어로 \x0a파일을 만들려고 해도 제대로 만들어지지 않아 이번에도 pwntools을 사용하였다.
그리고 process의 cwd을 통해 현재 경로를 /tmp 폴더로 지정해서 \x0a 파일을 읽도록 하였다.
1
2
3
4
5
6
7
|
// 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 |
1
2
3
4
5
|
p.run("mkdir /tmp/johg")
p.write('/tmp/johg/\x0a', "\x00\x00\x00\x00")
p2 = p.process(cwd='/tmp/johg',executable='/home/input2/input', argv=arg, stderr="/tmp/johg/err", env=en)
|
cs |
Stage 5
Socket 프로그래밍으로 포트를 argv['C']의 값으로 정해준다는게 중요할 것 같다.
그리고 해당 포트로 "\xde\xad\xbe\xef" 데이터를 전송하면 플래그를 떨궈주게 된다.
중요한 점은 flag 파일을 /tmp 폴더에 링크를 걸어줘야 한다! 왜냐하면 Stage 4에서 현재 경로를 /tmp로 했기 때문이다.
/tmp 폴더에는 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
|
// 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");
|
cs |
Pwntools 종합
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 * #context.log_level='debug' p = ssh(user="input2", host="pwnable.kr", port=2222, password="guest") p.run("mkdir /tmp/JHG") # Stage 2 p.write('/tmp/JHG/err', "\x00\x0a\x02\xff") # Stage 2 p.write('/tmp/JHG/\x0a', "\x00\x00\x00\x00") # Stage 4 en={'\xde\xad\xbe\xef':"\xca\xfe\xba\xbe"} # Stage 3 arg = [str(i) for i in range(100)] # Stage 1 arg[65] = "\x00" # Stage 1 arg[66] = "\x20\x0a\x0d" # Stage 1 arg[67] = "22222" # Stage 5 p2 = p.process(cwd='/tmp/JHG',executable='/home/input2/input', argv=arg, stderr="/tmp/JHG/err", env=en) # stage 1 print p2.recvuntil('Stage 1') payload="\x00\x0a\x00\xff" p2.send(payload) # stage 2 print p2.recvuntil('Stage 2') # stage 3 print p2.recvuntil('Stage 3') # stage 4 print p2.recvuntil('Stage 4') local=p.remote('localhost',22222) # Stage 5 local.send("\xde\xad\xbe\xef") # Stage 5 # stage 5 print p2.recvuntil('Stage 5') print p2.recv() # flag | cs |
이 문제는 가장 생각날 것 같다.
pwntools의 process 사용법에 대해서 더 알게 된 것 같으면서 여러가지 정보를 많이 배운 것 같다.
'리버싱 기초 > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] shellshock 풀이 (쉘숔 CVE-2014-6271) (0) | 2020.06.24 |
---|---|
[pwnable.kr] mistake 풀이 (연산자 순위) (0) | 2020.06.23 |
[pwnable.kr] random 풀이 (0) | 2020.06.22 |
[pwnable.kr] passcode 풀이 (scanf() 함수 취약점) (0) | 2020.06.22 |
[pwnable.kr] flag 풀이 (0) | 2020.06.22 |