-
-
[原创]Linux Kernel Exploit 内核漏洞学习(3)-Bypass-Smep
-
发表于: 2019-7-30 13:35 7151
-
smep的全称是Supervisor Mode Execution Protection
,它是内核的一种保护机制,作用是当CPU处于ring0模式的时候,如果执行了用户空间的代码就会触发页错误,很明现这个保护机制就是为了防止ret2usr攻击的....
这里为了演示如何绕过这个保护机制,我仍然使用的是CISCN2017 babydriver,这道题基本分析和利用UAF的方法原理我已经在kernel pwn--UAF这篇文章中做了解释,在这里就不再阐述了,环境也是放在github上面的,需要的可以自行下载学习....
ptmx
设备是tty
设备的一种,open
函数被tty
核心调用, 当一个用户对这个tty
驱动被分配的设备节点调用open
时tty
核心使用一个指向分配给这个设备的tty_struct
结构的指针调用它,也就是说我们在调用了open
函数了之后会创建一个tty_struct
结构体,然而最关键的是这个tty_struct
也是通过kmalloc
申请出来的一个堆空间,下面是关于tty_struct
结构体申请的一部分源码:
其中kzalloc
:
而正是这个kmalloc
的原因,根据前面介绍的slub分配机制,我们这里仍然可以利用UAF漏洞去修改这个结构体....
这个tty_struct
结构体的大小是0x2e0,源码如下:
而在tty_struct
结构体中有一个非常棒的结构体tty_operations
,其源码如下:
可以看到这个里面全是我们最喜欢的函数指针....
当我们往上面所open
的文件中进行write
操作就会调用其中相对应的int (*write)(struct tty_struct * tty,const unsigned char *buf, int count);
函数....
现在我们来说一下系统是怎么知道这个Smep
保护机制是开启的还是关闭的....
在系统当中有一个CR4寄存器
,它的值判断是否开启smep
保护的关键,当CR4寄存器
的第20
位是1的时候,保护开启;是0到时候,保护关闭:
举一个例子:
当CR4的值为0x1407f0的时候,smep
保护开启:
当CR4的值为0x6f0的时候,smep
保护开启:
但是该寄存器的值无法通过gdb直接查看,只能通过kernel crash时产生的信息查看,不过我们仍然是可以通过mov指令去修改这个寄存器的值的:
因为此题没有开kaslr保护,所以简化了我们一些步骤,但是在此方法中是我们前面的UAF
,ROP
和ret2usr
的综合利用,下面是基本思路:
poc.c:
编译:
运行:
这道题其实最关键的是要熟悉内核的执行流程,了解一些关键的结构体以及他们的分配方式;
最后这里说一下找mov_cr4_rdi_pop_rbp_ret
等这些gadget的小技巧,如果使用ropper或ROPgadget工具太慢的时候,可以先试试用objdump去找看能不能找到:
但是使用这个方法的时候要注意看这些指令的地址是不是连续的,可不可以用;用这个方法不一定可以找到iretq,还是需要用ropper工具去找,但是大多数情况应该都可以找到的:
struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) { struct tty_struct *tty; tty = kzalloc(sizeof(*tty), GFP_KERNEL); if (!tty) return NULL; kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); mutex_init(&tty->throttle_mutex); init_rwsem(&tty->termios_rwsem); mutex_init(&tty->winsize_mutex); init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->ctrl_lock); spin_lock_init(&tty->flow_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); tty->driver = driver; tty->ops = driver->ops; tty->index = idx; tty_line_name(driver, idx, tty->name); tty->dev = tty_get_device(tty); return tty; }
static inline void *kzalloc(size_t size, gfp_t flags) { return kmalloc(size, flags | __GFP_ZERO); }