본문 바로가기

pwnable/pwnable.kr

[pwnable.kr Toddler's Bottle] mistake write up

728x90

오늘은 mistake문제를 풀어보았다.

mistake@prowl:~$ ls -l
total 24
-r-------- 1 mistake_pwn root      51 Jul 29  2014 flag
-r-sr-x--- 1 mistake_pwn mistake 8934 Aug  1  2014 mistake
-rw-r--r-- 1 root        root     792 Aug  1  2014 mistake.c
-r-------- 1 mistake_pwn root      10 Jul 29  2014 password

소스코드, 취약 파일, flag 외에 password라는 하나의 파일이 더 존재한다.

일단 mistake.c 소스코드부터 보도록 하자.

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

처음에 코드를 보면서 정리를 했을 때 다음과 같았다.

  • /home/mistake/password파일을 열고 제대로 연 경우 fd를 받는다.
  • brute force를 막기 위해 random 하게 긴 시간을 sleep 한다.
  • pw_buf에 10바이트만큼 fd(password)에서 읽어온다.
  • 키보드로 pw_buf2에 10바이트만큼 읽어온 후 1과 xor 시킨다.
  • pw_buf와 xor 시킨 pw_buf2와 같으면 cat flag를 수행

하지만 password 안에 무엇이 있는지 알 수가 없었다. 혹시나 하는 마음에 링크 파일을 만들어보려고 해 봐도 권한 때문에 불가능했다. pw_buf2에도 10바이트만큼 읽도록 해놓은 마당에 오늘도 멘붕을 경험하던 중에 왜 이번 단계 이름이 mistake일까 생각을 해봤다. 처음에 봤을 때에는 문제가 없어 보였기 때문에, 다시 한번 살피던 도중에 허탈하게 찾았다.

 

다음 두 부분을 보면 평소에 코딩할 때와 다른 점이 있다.

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
        printf("can't open password %d\n", fd);
        return 0;
}
if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
        printf("read error\n");
        close(fd);
        return 0;
}

첫 번째 코드부터 보자면 필자는 "home/mistake/password"의 파일 디스크립터를 fd로 받고 음수인지 체크하는 것으로 생각했지만, 괄호가 빠져있다. 즉 연산 우선순위에 의해서 fd = (open("/home/mistake/password", O_RDONLY, 0400) < 0) 이 되는 것이다! password파일이 RD로 정상적으로 열린다면 fd = ([password의 파일 디스크립터] < 0)이 되므로 fd = 0이 되며, password파일이 열리지 않았을 경우 fd = (-1 < 0)이 되기 때문에 fd = 1이 되게 된다. 또한 fd가 1이면 if문 안으로 진입하게 되므로 프로그램이 종료되게 된다.

두 번째 코드 역시 이어서 password파일이 정상적으로 열리게 되면 fd가 0이 되기 때문에 stdin을 받게 된다. 또한 여기서도 괄호가 빠져서!(len = (read(fd, pw_buf, PW_LEN) > 0))이 된다. 즉, 키보드로 입력받은 길이가 0보다 클 경우에 len=1이 되며, 부정(!)에 의해 if문 안으로 진입을 안 하게 된다!

 

다시 한번 정리하면 다음과 같게 된다.

 

  • /home/mistake/password파일이 열릴 경우 fd = 0(stdin).
  • brute force를 막기 위해 random 하게 긴 시간을 sleep 한다.
  • pw_buf에 10바이트만큼 stdin에서 읽어온다.
  • 키보드로 pw_buf2에 10바이트만큼 읽어온 후 1과 xor 시킨다.
  • pw_buf와 xor 시킨 pw_buf2와 같으면 cat flag를 수행

 

따라서 비교시킬 두 문장을 직접 입력해주면 되기 때문에 그다음부터는 바로 하기 쉬웠다...!

 

mistake@prowl:~$ ./mistake
do not bruteforce...
1111111111
input password : 0000000000
Password OK
Mommy, the operator priority always confuses me :(

 

잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다

728x90