ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [pwnable.kr][Toddler's Bottle] 7. input
    Wargame/pwnable.kr 2019. 7. 26. 22:27

    Toddler's Bottle - input

     

    ID: input2, PW: guest로 접속한 후 파일을 확인해보니 input 파일이 있었다.

    ls

     

    input.c 파일을 확인해보았다.

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

    확인해보면 엄청 긴 코드가 있다. 5개의 stage를 만족하면 flag가 출력된다고 한다. 각 stage별로 코드를 확인해보자.

     

    stage 1. argv

    argc는 인자의 개수, argv는 각 인자의 문자열을 말한다.

    stage 1. argv

    stage 1을 통과하기 위해서는,

    1) 인자의 개수 = 100

    2) argv['A'] == \x00 : 65번째 인자 = \x00

    3) argv['B'] == \x20\x0a\x0d : 66번째 인자 = \x20\x0a\x0d

    이 세 가지 조건을 만족해야 한다.

     

    stage 2. stdio

    stage 2는 fd(file descriptor)와 관련된 조건이다.

    stage 2. stdio

    stage 2를 통과하기 위해서는,

    1) 표준 입력(stdin) = \x00\x0a\x00\xff

    2) 표준 오류(stderr) = \x00\x0a\x02\xff

    를 만족해야 한다.

     

    stage 3. env

    stage 3는 환경변수와 관련된 스테이지이다.

    stage 3. env

    \xde\xad\xbe\xef라는 환경변수에 \xca\xfe\xba\xbe를 넣어주면 stage 3을 통과할 수 있다.

     

    stage 4. file

    stage 4에서는 \x0a라는 파일을 읽기 전용으로 open한다. 그 다음, open한 파일의 첫 4 bytes가 \x00\x00\x00\x00이면 stage 4를 클리어할 수 있다.

    stage 4. file

     

    stage 5. network

    stage 5는 소켓에 관련된 스테이지이다. 

    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; //IPv4
        saddr.sin_addr.s_addr = INADDR_ANY; //32bit IPv4 주소
        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//소켓으로부터 수신한 바이트 수 == 4 bytes
        if(memcmp(buf, "\xde\xad\xbe\xef"4)) return 0//버퍼 == \xde\xad\xbe\xef
        printf("Stage 5 clear!\n");
     

    atoi(argv['C']), 즉 67번째 인자를 정수형으로 변환한 값이 포트 번호가 된다. 해당 포트 번호로 서버 소켓을 연 다음\xde\xad\xbe\xef를 전송하면 stage 5가 클리어될 것이다.

     

     

    이제 flag를 얻기 위한 모든 조건을 알아냈다. 그런데 이 조건을 만족하는 인자를 한번에 전달하려면 따로 익스플로잇 코드를 작성해야 해서 다른 분들의 코드를 참고했다...

     

    코드 작성은 /home/input2 디렉토리에서 불가능하므로 tmp 디렉토리에 새 디렉토리를 생성한 후 그 안에서 코드를 작성해야 한다.

    /tmp/toatnd85

    작성한 코드는 아래와 같다.

    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
    from pwn import *
     
    #stage 1
    argvs = [str(i) for i in range(100)] #인자 100개 채움
    argvs[ord('A')] = '\x00' #65번째 인자
    argvs[ord('B')] = '\x20\x0a\x0d' #66번째 인자
     
    #stage 2: 표준오류 조건
    with open('./stderr''a') as f:
            f.write('\x00\x0a\x02\xff')
     
    #stage 3: 환경변수 설정
    env_var = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
     
    #stage 4: \x0a 파일 생성
    with open('./\x0a''a') as f:
            f.write('\x00\x00\x00\x00')
     
    #stage 5: 포트번호 설정
    argvs[ord('C')] = '40000'
     
    #인자, 환경변수, 실행할 파일 설정
    target = process(executable='/home/input2/input', argv=argvs, stderr=open('./stderr'), env=env_var)
     
    #stage 2: 표준입력 조건
    target.sendline('\x00\x0a\x00\xff')
     
    #stage 5: 열린 소켓을 통해 데이터 전송
    conn = remote('localhost'40000)
    conn.send('\xde\xad\xbe\xef')
    conn.close()
     
    target.interactive()
     

     

    코드를 다 작성하면 작성한 프로그램을 실행한다. 그런데, 파일을 실행해도 flag가 보이지 않는다.

    exploit.py 실행

     

    현재 디렉토리에 flag 파일이 없기 때문에 작성한 익스플로잇을 실행해도 flag가 나타나지 않는 것이다. 그래서 flag 파일에 대해 심볼릭 링크를 걸어준 다음에 다시 파일을 실행하니 flag가 나타났다.

    flag: Mommy! I learned how to pass various input in Linux :)

     

    [참고 자료]

    socket( ) 소켓 생성: http://forum.falinux.com/zbxe/index.php?document_srl=429387&mid=C_LIB

    recv( ) 소켓으로부터 자료 수신: http://forum.falinux.com/zbxe/index.php?document_srl=441107&mid=C_LIB

    pwntools 사용법: https://lclang.tistory.com/90?category=254640https://lclang.tistory.com/82?category=254640

    익스플로잇 코드: https://gmltnscv.tistory.com/27https://mandu-mandu.tistory.com/76

     

Designed by Tistory.