Rootme Race condition (ch12)

2018. 5. 2. 19:430x04 pwnable

728x90

Race condition이란?


한정된 자원을 "동시에" 이용하려는 여러 프로세스가 자원의 이용을 위해 경쟁을 벌인다.


레이스컨디션을 이용하면 root 권한을 얻을 수 있게 된다.


Race condition은 어떻게 발생하게 될까?


심볼릭 링크에 대해 조금 알고 있어야 한다. 


심볼릭 링크란 바로가기라고 볼 수 있는데, 심볼릭 링크가 수정되어도 본래의 파일은 수정이 되게 된다.



rootme문제를 통해 Race condition을 이해해보자.


접속

ssh -p 2222 app-systeme-ch12@challenge02.root-me.org


id = app-systeme-ch12

pw = app-systeme-ch12



Race condition의 조건 

1. 다른 계정의 권한에 접근해야 하기 때문에 SUID가 걸려 있어야 한다.

2. 임시 파일을 생성해야한다.

3. 공격자는 해당 임시 파일 명을 정확히 알아야 한다. (리버싱으로 알아낼 수가 있다)


나는 소스코드 대신 바이너리만 있다고 가정하고 문제를 풀어보려 한다.


 우선 SSH에 접속하여 바이너리를 획득한다.


scp -P2222 chanllenge02.root-me.org://challenge/app-systeme/ch12/ch12 ./ 




바이너리를 열어보았다.

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <sys/ptrace.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <stdlib.h>
  9.  
  10. #define PASSWORD "/challenge/app-systeme/ch12/.passwd" // 플래그 있는 위치 
  11. #define TMP_FILE "/tmp/tmp_file.txt"  // 임시파일 경로 및 파일 명 
  12.  
  13. int main(void)
  14. {
  15.   int fd_tmp, fd_rd;
  16.   char ch;
  17.  
  18.  
  19. //  if ((fd_tmp = open(TMP_FILE, O_RDONLY)) != NULL )
  20. //  {
  21. //     close(fd_tmp);
  22. //     return 1;
  23. //  }
  24.    
  25.   if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0)
  26.     {
  27.       printf("[-] Don't use a debugguer !\n");
  28.       abort();
  29.     }
//    해당 경로 /tmp/tmp_file.txt 가 없으면 에러 출력
  1.   if((fd_tmp = open(TMP_FILE, O_WRONLY | O_CREAT,0444)) == -1)
  2.     {
  3.       perror("[-] Can't create tmp file ");
  4.       exit(0);
  5.     }SU
  6.    
  7.   if((fd_rd = open(PASSWORD, O_RDONLY)) == -1)
  8.     {
  9.       perror("[-] Can't open file ");
  10.       exit(0);
  11.     }
  12.    
  13. // 읽어온다.
  14.   while(read(fd_rd, &ch, 1) == 1)
  15.     {
  16.       write(fd_tmp, &ch, 1);
  17.     }
  18.   close(fd_rd);
  19.   close(fd_tmp);
  20.   usleep(250000); // 시간이 걸린다. 
    1. // 즉, 해당 파일이 삭제되기 전까지 시간이 존재한다는 것 
      1. // 이 부분을 사용해서 해킹을 해야 한다.
  21.   unlink(TMP_FILE); // 여기서 unlink => 임시파일 삭제 
  22.    
  23.   return 0;
  24. }



TMP_FILE : /tmp/tmp_file.txt 이게 있는지 확인해본다 

ls -al /tmp | grep tmp_file.txt


tmp에 접근도 할 수 없었고, cat 명령어를 통해서도 보았지만, tmp_file.txt는 존재 하지 않음을 보았다.


이제 바이너리를 실행해보았다.


바이너리 실행 후 바로 unlink로 임시파일을 삭제하기 때문에 tmp_file.txt는 보이지 않았다.


gdb를 통해 usleep에 breakpoint를 걸었다.




ptrace(PTRACE_TRACEME, 0, 1, 0) < 0)

이 함수 때문에 debugging이 되지 않는다. 

gdb에서 일시적으로 패치를 진행하면 이를 우회할 수 있게 된다. 


ptrace의 반환 값 eax를 변경하자 


ptrace에 먼저 bp를 건다




그 후 ni를 통해 반환 결과를 확인한다.



현재 eip는 0x80485f1을 가리키고 있다. 

그렇다면, ptrace함수 호출이 끝나고 반환 값을 받았다는 의미가 되며, x86에서는 eax가 함수의 반환값이 된다. 

지금 eax는 0xffffffff(-1)이다. 

현재 이 값은 여기에 만족하게 된다.


  1.   if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0)
  2.     {
  3.       printf("[-] Don't use a debugguer !\n");
  4.       abort();
  5.     }

그렇기 때문에 test 명령이 수행되기 전에 eax 값을 우회해주어야 한다. 

이러한 방법은 윈도우즈에서 안티디버깅 중 isDebuggerPresent에서도 사용가능하다.


set $eax=1


이제 test연산을 만나게 되면, 이 주소로 점프하게 된다.



만약, test 연산 결과가 -1이었다면, 다음 사진의 주소로 점프하게 될 것이다.




Don't use a debugger를 우회하였기에 이제 맘 편히 디버깅으로 분석할 수 있게 되었다.


이어서, ulink전에 usleep부분에 bp를 걸어두었다.



그 후 c 명령을 통해 continue 해보면, breakpoint 0x804883d에서 디버깅은 pause상태가 될 것이다.


하지만, 결과는 이렇게 나오게 된다.



이유는 다음과 같다.

  1.  if((fd_rd = open(PASSWORD, O_RDONLY)) == -1)
  2.     {
  3.       perror("[-] Can't open file ");
  4.       exit(0);
  5.     }

지금 PASSWORD가 열리지 않았다. 

이유는 다음과 같다.

#define PASSWORD "/challenge/app-systeme/ch12/.passwd" // 플래그 있는 위치 



whoami를 통해 현재 사용자를 보면 app-systeme-ch12인데 

.passwd 파일의 소유주는 app-systeme-ch12-cracked 이기 때문에 접근을 할 수가 없다.


그렇다면, 이런 문제는 어떻게 풀어야 할까?


여기 전체를 보면 알 수 있다.

  1. // fd_tmp를 파일 존재 여부에 사용하게 되는데 TMP_FILE을 CHMOD : 0444 권한을 통해 생성한다.
  2.   if((fd_tmp = open(TMP_FILE, O_WRONLY | O_CREAT,0444)) == -1)
  3.     {
  4.       perror("[-] Can't create tmp file ");
  5.       exit(0);
  6.     }
  7.    
  8. // fd_rd를 파일 존재 여부에 사용하게 되는데 PASSWORD파일이 있는지 파악할 수 있다.
  9.   if((fd_rd = open(PASSWORD, O_RDONLY)) == -1)
  10.     {
  11.       perror("[-] Can't open file ");
  12.       exit(0);
  13.     }


  1. // 읽어온다.
  2. // TMP_FILE 즉 /tmp/tmp_file.txt에 생성을 성공하고
  3. // PASSWORD 즉 플래그가 있는 파일까지 읽게 되면 
  4. // ch 버퍼에 저장 한뒤 , fd_tmp에 복사를 진행할 수 있다 .
  5.   while(read(fd_rd, &ch, 1) == 1)
  6.     {
  7.       write(fd_tmp, &ch, 1);
  8.     }



분석을 어느정도 마쳤으니 플래그를 슬슬 획득하러 가보자.

Race condition으로 풀 수 있다. 


다시 한번, Race condition을 복습하면 

소유주가 root권한인 하나의 파일을 해커가 몰래 symbolic link를 만들어둔다음 소유주가 symbolic link파일을 수정하게 되면 실제 파일 역시 수정이 되는 취약점이 존재한다.


코드 작성을 조금 해야한다. 


첫 번째 코드 : "타깃 계속 실행하는 코드" 

두 번째 코드 : "타킷에서 생성하게 되는 임시 파일에 접근하는 코드"




'0x04 pwnable' 카테고리의 다른 글

리눅스 커널 공부 1  (0) 2018.05.04
Rootme ch12 (Race condition)  (0) 2018.05.02
고수준 파일 입출력 공부  (0) 2018.04.29
저수준 파일 입출력 공부  (0) 2018.04.29
Protostar Simple Writeup  (0) 2018.04.10