ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [pwnable.kr][Toddler's Bottle] 9. mistake
    Wargame/pwnable.kr 2019. 8. 4. 03:41

    Toddler's Bottle - mistake

     

    ID: mistake, PW: guest로 로그인한 후 안에 있는 파일을 확인해보았더니 flag와 mistake 파일, password 파일이 있었다.

    ls -l

    mistake.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
    #include <stdio.h>
    #include <fcntl.h>
     
    #define PW_LEN 10
    #define XORKEY 1
     
    void xor(char* s, int len){
        int i;
        for(i=0; i<len; i++){
            s[i] ^= XORKEY;
        }
    }
     
    int main(int argc, char* argv[]){
        
        int fd;
        if(fd=open("/home/mistake/password",O_RDONLY,0400< 0){
            printf("can't open password %d\n", fd);
            return 0;
        }
     
        printf("do not bruteforce...\n");
        sleep(time(0)%20);
     
        char pw_buf[PW_LEN+1];
        int len;
        if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
            printf("read error\n");
            close(fd);
            return 0;        
        }
     
        char pw_buf2[PW_LEN+1];
        printf("input password : ");
        scanf("%10s", pw_buf2);
     
        // xor your input
        xor(pw_buf2, 10);
     
        if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
            printf("Password OK\n");
            system("/bin/cat flag\n");
        }
        else{
            printf("Wrong Password\n");
        }
     
        close(fd);
        return 0;
    }
     

     

    문제 이름이 mistake인 것으로 보아, 해당 코드에 어떤 실수가 있을 것이다. 위 소스 코드에서 실수한 것은 바로 연산자 우선순위를 고려하지 않은 것이다.

     

    아래 코드를 보면, 대입 연산자인 등호(=)와 비교 연산자인 부등호(<)가 섞여서 사용되었다. 대입 연산자는 비교 연산자보다 우선순위가 낮으므로, 아래 코드의 경우 open 함수의 리턴값과 0을 비교한 결과값이 fd에 들어가게 될 것이다. 

    1
    2
    3
    4
    5
    int fd;
    if(fd=open("/home/mistake/password",O_RDONLY,0400< 0){
        printf("can't open password %d\n", fd);
        return 0;
    }
     

    open 함수는 성공했을 경우 양의 정수를 반환하고, 실패하면 -1을 반환한다. 이 경우에는 password 파일을 여는 것에 성공해 양의 정수를 반환하므로, (open 함수의 리턴값) < 0 은 false가 되어 fd에는 0이 저장된다.

     

    위와 같은 실수는 아래에서도 반복된다.

    1
    2
    3
    4
    5
    if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
            printf("read error\n");
            close(fd);
            return 0;        
    }
     

    read 함수에서 fd가 0이므로 표준입력(stdin)을 수행하게 되고, 그 입력값은 pw_buf에 저장된다. 또한 read 함수가 성공적으로 수행되면 읽어들인 바이트 수를 반환하고, 실패하면 -1을 반환한다. 만약 성공적으로 수행된다면 (read 함수의 리턴값) > 0은 true가 되므로 len에는 1이 저장될 것이고, 결국 if문의 조건을 만족하지 않아 read error 문구는 출력되지 않고 넘어갈 것이다.

    여기서 중요한 점은 read 함수의 fd가 0이므로 표준 입력을 통해 pw_buf에 내가 원하는 값을 넣을 수 있다는 것이다.

     

    뒷부분의 코드를 통해 알 수 있듯이, scanf를 통해 pw_buf2에도 내가 원하는 값을 넣을 수 있으므로, pw_buf1의 값과 pw_buf2을 XOR한 값이 같도록 입력값을 주면 된다.

     

    그래서 나는 pw_buf1의 값을 1111111111로 주고, pw_buf2의 값을 0000000000으로 주었더니 flag가 나타났다.

     

    flag: Mommy, the operator priority always confuses me :(

Designed by Tistory.