본문 바로가기
pwnable

[Toddler's Bottle] mistake

by 미스터 J 2024. 5. 11.
반응형

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


1점 짜리 문제치고는 코드가 너무긴데..? 당황하지 않고 차례대로 살펴보자

 

read 함수의 사용법을 다시 한 번 복습과 더불어 모르는 내용을 복습하자

ssize_t read(int fd, void *buf, n_bytes) - fd가 참조하는 파일의 오프셋에서 len 바이트만큼 buf로 읽어 들임

        ssize_t : 파일 읽기 성공 - 0보다 큰수 // 읽을 데이터 없을때 - 0

        fd : 데이터를 전송해 주는 대상을 가리키는 파일 디스크립터 (0 : 표준입력 1: 표준출력 2: 표준에러출력)

        buf : 수신한 데이터를 저장할 버퍼를 가리키는 포인터 (저장할 메모리 공간)

        nbytes : 수신할 최대 바이트 수 (읽을 데이터의 크기)

sleep()함수 : 딜레이를 주는 함수

int open(const char *pathname , int flags , mode_t mode) : 파일 오픈하는 시스템 함수

파일을 성공적으로 열었다면 파일 지정번호를 이용. 0보다 작은값이 반환될 경우 파일열기에 실패한 경우

flags : 파일을 어떤 방식으로 열것인지 선택

 flags 설명 
 O_RDONLY  읽기 전용
 O_WRONLY  쓰기 전용
 O_RDWR  읽기 , 쓰기 모두
 O_CREAT  파일없을 경우 파일 생성
 O_EXCL  파일 존재시 error 리턴

 

mode : 파일 권한을 설정

 mode 설명 
 S_IRWXU  00700 / 파일소유자에게 읽기,쓰기,실행권한 부여 
 S_IRUSR   00400 / 사용자에게 읽기 권한 부여
 S_IWUSR  00200 / 사용자에게 쓰기 권한 부여
 S_IXUSR  00100 / 사용자에게 실행 권한 부여
 S_IRWXG  00070 / 그룹에게 읽기,쓰기,실행권한 부여
 S_IRGRP  00040 / 그룹에게 읽기권한 부여
 S_IWGRP  00020 / 그룹에게 쓰기권한 부여
 S_IXGRP  00010 / 그룹에게 실행권한 부여
 S_IRWXO  00007 / 기타 사용자에게 읽기,쓰기,실행권한 부여
 S_IROTH  00004 / 기타 사용자에게 읽기권한 부여
 S_IWOTH  00002 / 기타 사용자에게 쓰기권한 부여
 S_IXOTH  00001 / 기타 사용자에게 실행권한 부여

 

strcmp(const char* str1, const char* str2) : str1과 str2를 비교하는 함수 (string.h 필요)

        1) str1 < str2 인 경우 : -1 반환

        2) str1 > str2 인 경우 : 1 반환

        3) str1 == str2 인 경우 : 0 반환

strncmp(const char* str1, const char* str2, size_t n) :  str1, str2을 비교하지만 unsigned int로 되어있는 길이를 비교.

n 값이 5라면 str1,str2의 길이에 상관없이 5만 비교.

 

우선 main문을 보면,

fd가 0보다 작으면 Readonly인 password파일을 열수 없다고 출력하는 것 같다.

다만 open()함수로 파일 열기에 성공하면 파일 지정번호를 fd에 저장한다고 한다. 잠깐 실험을 해봐야겠다.

3이 출력된다. 이걸 0으로 바꿔줄수 있을까? 다시 보니까 아무래도 fd를 0으로 만들어주는 건 아닌것같다.

 

pw_buf 와 pw_buf2 를 10자 비교하는 것을 보니까 pw_buf에 원래 비밀번호가 들어있는것 같다.

0x00000000004008f8 <+167>:   call   0x400720 <sleep@plt>

0x00000000004008fd <+172>:   lea    rcx,[rbp-0x30]

0x0000000000400963 <+274>:   lea    rdx,[rbp-0x20]

sleep()함수 다음에 lea가 온것을 보니 rcx레지스터에 비밀번호가 저장되는듯하다. 심지어 초기화도 안되어있으니?

그리고 rbp기준으로 10의 차이가 나는 것을 보니 22바이트가 차이난다.

브레이크를 걸고 실행해보니까 fd는 1이라는 파일 지정번호로 저장되어있다.

0x000000000040099b <+330>:   call   0x400670 <strncmp@plt>

이건 strncmp 비교문이다.

다만 브레이크포인트를 계속 바꿔도 레지스터의 값은 확인 되지 않는다.

 

여기서 또 실수를 할 뻔했다.

scanf("%10s", pw_buf2); 이므로 char pw_puf2에는 이미 지정된 주소가 있을 것이다. pw_buf1이 아니라 pw_buf2를 봤어야 했다.

bp를 잡아도 다른 정보는 나오지 않는다.

 

다음부터는 write-up을 참고한다.

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)

을 먼저보면, fd에 입력하는 것처럼 보이지만 비교연산자 우선순위가 < 이 = 보다 높기 때문에 open함수의 반환값과 0을 비교한 결과가 fd에 들어가는 것이다!

위 코드가 실행될 때 open함수에서 파일이 정상적으로 열려서 양수를 반환하게 된다. 그리고 0과 비교했을 때 Fasle 이므로 fd에는 0이 들어가게 된다.

if(!(len=read(fd,pw_buf,PW_LEN) > 0))

일단 fd에 0이 들어갔다. read함수에서 fd = 0이면 stdin을 의미한다. 사용자로부터 입력값을 받는다.

입력값이 pw_buf에 들어가기 때문에 pw_buf의 값을 우리 마음대로 설정할 수 있다!

scanf("%10s", pw_buf2);

를 통해 pw_buf2의 값을 입력받기 때문에 pw_buf, pw_buf2 모두 우리 모두 설정할 수 있다.

그 뒤에 pw_buf2를 1과 xor한 값과 pw_buf가 같으면 플래그를 뿌려주게 된다.

 

.....허허... 허탈하다;; 1점짜리 문제가 맞긴하구나.

Mommy, the operator priority always confuses me :(

반응형

'pwnable' 카테고리의 다른 글

[Toddler's Bottle] passcode  (0) 2024.05.11
[Toddler's Bottle] random  (0) 2024.05.11
[Toddler's Bottle] shellshock  (0) 2024.05.11
[Toddler's Bottle] black jack  (0) 2024.05.11
[Toddler's Bottle] cmd1  (0) 2024.05.11