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