오늘은 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 :(
잘못된 점이나 부족한 점 지적해주시면 감사하겠습니다
'pwnable > pwnable.kr' 카테고리의 다른 글
[pwnable.kr Toddler's Bottle] memcpy write up (0) | 2019.08.25 |
---|---|
[pwnable.kr Toddler's Bottle] uaf write up (0) | 2019.08.22 |
[pwnable.kr Toddler's Bottle] blackjack write up (0) | 2019.08.22 |
[pwnable.kr Toddler's Bottle] coin1 write up (0) | 2019.08.18 |
[pwnable.kr Toddler's Bottle] shellshock write up (0) | 2019.08.17 |