Self-Improvement

[pwnable.kr] input 풀이 (pwntools process executable!!) 본문

리버싱 기초/pwnable.kr

[pwnable.kr] input 풀이 (pwntools process executable!!)

JoGeun 2020. 6. 23. 13:55

소스코드

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 != 100return 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, 41, 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, 40!= 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 != 100return 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
= 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, 41, 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, 40!= 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'
 
= 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 사용법에 대해서 더 알게 된 것 같으면서 여러가지 정보를 많이 배운 것 같다.