System&Write up/Pwnable.kr

[pwnable.kr] Toddler's Bottle - input

Jubil 2018. 1. 20. 14:22
반응형

[pwnable.kr] Toddler’s Bottle - 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 != 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;

}

 

Colored by Color Scripter

cs

 

 

많은 inputstage별로 각각 다 컨트롤 할 수 있는지 test하는 문제입니다.

 

일단 pwnable.kr/tmp에 자기 이름 폴더를 만들고 문제를 풀겠습니다.

 

code:

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

 

Stage 1을 보면 argcargv를 잘 맞춰줘야 합니다.

 

 

pwntools에서 argvlist를 써서 넣어줄 수 있습니다.

 

exploit:

1

2

3

4

5

6

#argvs = range(100)

argvs = [str(i) for i in range(100)]

argvs[ord('A')] = "\x00"

argvs[ord('B')] = "\x20\x0a\x0d"

 

= process(executable='/home/input2/input', argv=argvs)

cs

 

처음에는 range(100)로 만들었는데, process 함수에서 오류가 나서 전체가 다 문자로 이루어진 리스트를 만들었습니다.

 

그리고 process에서 executable에 공격할 파일 경로를 주고, argv에 넣을 인자 리스트를 줍니다.

 


 

Stage 1 clear!

 

code:

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

 

Stage 2에서는 stdio를 컨트롤해야 합니다.

 

read 함수의 첫 번째 파라미터 02는 각각 stdin, stderr를 가리킵니다.

 

그래서 stdin은 그냥 sendline 함수로 전달해줄 수 있고, stderr process 함수에서 사용할 stderr 파일을 새로 만들어서 그 파일의 fd를 넣어주면 됩니다. (원래 기본으로 2가 설정되어 있음.)

 

exploit:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#stderr_file_fd = os.open('./stderr_file', os.O_RDWR|os.O_CREAT)

#stderr_file_obj = os.fdopen(stderr_file_fd, 'w+')

#stderr_file_obj.write("\x00\x0a\x02\xff")                use os.fdopen()

 

#stderr_r, stderr_w = os.pipe()

#os.write(stderr_w, "\x00\x0a\x02\xff")                    use os.pipe()

 

with open('./stderr_file''w+') as f:

    f.write("\x00\x0a\x02\xff")

 

stderr_file_fd = os.open('./stderr_file', os.O_RDWR|os.O_CREAT)

 

= process(executable='/home/input2/input', argv=argvs, stderr=stderr_file_fd)

 

s.sendline("\x00\x0a\x00\xff")

time.sleep(0.1)

print s.recv()

 

os.close(stderr_file_fd)

Colored by Color Scripter

cs

 

ostimeimport 했습니다.

stdinsendline을 이용해서 처리했고, stderros.fdopen()으로 파일 제어, os.pipe()를 이용한 통신 등을 이용해봤다.

 

 


 

Stage 2 clear!

 

code:

1

2

3

// env

    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;

    printf("Stage 3 clear!\n");

Colored by Color Scripter

cs

 

Stage3에서는 env를 컨트롤해야 합니다.

“\xde\xad\xbe\xef”의 이름의 환경 변수의 값이 “\xca\xfe\xba\xbe"여야 합니다.

 

pwntools에서 환경 변수는 딕셔너리를 이용해서 넣어주면 됩니다.

 

exploit:

1

2

3

env_var = { '\xde\xad\xbe\xef' : '\xca\xfe\xba\xbe' }

 

= process(executable='/home/input2/input', argv=argvs, stderr=stderr_file_fd, env=env_var)

cs

 

딕셔너리를 만들고 env에 전달했습니다.

 

 


 

Stage 3 clear!

 

code:

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

 

 

Stage 4에서는 file을 컨트롤해야 합니다.

 

“\x0a”라는 이름을 가진 파일의 내용이 “\x00\x00\x00\x00”이여야 합니다.

코드를 짜보도록 하겠습니다.

 

exploit:

1

2

with open('./\x0a''w+') as f:

    f.write("\x00\x00\x00\x00")

cs

 

파일을 만들고 값을 넣어줬습니다. with ~ as문이라서 close()를 따로 해줄 필요가 없습니다.

 


 

Stage 4 clear!

 

 

뒤에 bind error가 나오는데 Stage 5에 관련된 에러입니다. 한 번 보도록 하겠습니다.

 

code:

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

// 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;

}

Colored by Color Scripter

cs

 

Stage 5에서는 network를 컨트롤해야 합니다.

 

argv[‘C’]에 있는 값을 atoi()를 이용해서 숫자로 바꾼 뒤 포트 번호로 사용합니다.

그리고 4byte를 입력 받고 그 값이 “\xde\xad\xbe\xef”clear 할 수 있습니다.

 

아까 bind 에러가 떴으니 argv[‘C’]의 값을 바꿔줍시다.

 


 

포트 번호를 6666으로 바꾸고 실행해봅니다.

 


 

이제 에러가 뜨지 않습니다.

그럼 “\xde\xad\xbe\xef”를 전달해 줍니다.

 

 

1

2

stage5_socket = remote('localhost'6666)

stage5_socket.sendline("\xde\xad\xbe\xef")

cs

 

6666 포트에 연결하고 값을 전달해줬습니다.

 


 

Stage 5 clear!

 

clear 했습니다. 하지만 제 디렉터리에는 flag가 없기 때문에 값을 읽어올 수 없는데요. 심볼릭 링크를 걸도록 하겠습니다.

 


 

flag를 얻었습니다.

반응형