Pwnable KR - mistake

문제

Pwnable KR mistake

힌트는 우선순위

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

풀이

코드의 흐름은 password 파일로부터 문자를 읽어와 pw_buf 배열에 담고, 사용자로부터 pw_buf2 배열에 문자를 받아와 비교하는 것이다.

(gdb) r
Starting program: /home/mistake/mistake
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7fffc83fe000
can't open password 1
[Inferior 1 (process 31505) exited normally]

그렇다면 중간에 브레이크포인트를 걸고 메모리를 고친다면 해결할 수 있지 않을까? 그런데 gdb로 불러온 상황에서는 진행이 되지 않는다. 다른 방법을 찾아야한다.

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

힌트를 생각하면서 if문을 보면 이상한 점을 볼 수 있다. 비교 연산자는 산술 연산자보다 우선순위가 높다. 원래 의도는 fd 변수에 open 함수의 반환값을 할당하고 그 값을 0보다 작은지 비교하려고 했을 것이다.

하지만 이 코드에서는 open 함수의 반환값을 0과 비교하여 나온 값을 fd에 할당하게 된다. 의도대로 기능하려면 다음과 같이 작성됐어야 한다.

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

다음에 나오는 if문도 마찬가지로 우선순위를 고려하지 않고 작성됐다. 여기서 주목할 점은 이 if문을 실행할 때의 상황에서 fd 변수에 저장된 값은 항상 0이라는 점이다.

read 함수의 첫 인자에 0이 들어가면 표준입력으로부터 데이터를 받는다. 이 데이터는 사용자가 입력하게 되는데 대략 이런 느낌이다.

scanf("%s", pw_buf);
if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
    printf("Password OK\n");
    system("/bin/cat flag\n");
}
else{
    printf("Wrong Password\n");
}

pw_buf와 pw_buf2 모두 사용자가 데이터를 입력할 수 있고, 최종적으로 pw_buf와 pw_buf2의 내용이 동일할 때 flag의 값을 볼 수 있다.

xor(pw_buf, 10);
void xor(char* s, int len){
    int i;
    for(i=0; i<len; i++){
        s[i] ^= XORKEY;
    }
}

pw_buf2는 xor 함수에서 변환을 거치는데, 한 문자씩 비트 XOR 연산을 한다. 사용자가 “1111111111"을 입력하면 “0000000000"으로 바뀌고, “bbbbbbbbbb"를 입력하면 “cccccccccc"로 바뀌게 되는 것이다.

이것을 고려하고 pw_buf와 pw_buf2에 각각 값을 입력하면 문제의 답을 알 수 있다.