首页
社区
课程
招聘
[原创] CVE-2017-8890 漏洞利用(root ubuntu@kernel-4.10.0-19)
发表于: 2019-1-29 11:41 15805

[原创] CVE-2017-8890 漏洞利用(root ubuntu@kernel-4.10.0-19)

2019-1-29 11:41
15805

本文主要参考thor的文章,完成了在ubuntu16.04.1@kernel 4.10.0-19 上的root利用。内容主要是总结一下目前的利用思路(ret2user和bypass smep)以及在实践时遇到的一些小问题。

根据上篇 CVE-2017-8890 漏洞分析的文章,最后已经触发了漏洞:在第二次free时,内核发生了panic。再看一下这个vulnerable obj:
Alt text
Alt text
obj释放的过程:
Alt text

触发内核panic后,由于是double free的漏洞,两次free的时机都可控。因此利用的思路很简单,在第一次释放后,通过heap spray的技术来占位被释放的obj,从而在第二次free的时候,尝试利用占位的数据去劫持EIP。

如何在第二次free时利用占位的数据控制EIP呢?这个和ip_mc_socklist的定义有关,由于这个结构体中定义了struct rcu_head rcu;因此,对于该结构体的读和写都受到linux kernel rcu机制[4]的保护。释放的过程也可以理解为一个写的过程。受到rcu机制保护的结构体在释放时,也就是上图中调用kfree_rcu(kfree an object after a grace period)时,并不是真正的释放,而是调用call_rcu把他加入到rcu_head的链表中。Alt text
根据对RCU机制粗略的学习了解到,此时会开始标记一个宽限期(GP)。当宽限期开始时,记录所有的读thread,当这些读thread都结束后,时钟中断触发时,在软中断中会调用rcu的回调函数来删除这个obj。

所以真正的释放过程是在rcu的回调函数中触发的。删除时的调用链如下rcu_do_batch->__rcu_reclaim,在__rcu_reclaim中会检查func的大小是否小于4096,如果小于4096,则释放,否则便会调用func。
Alt text
因此,如果可以控制func便可以控制EIP。因此,堆喷时的目标也就变成了占位func。

在gdb动态调试时,想手工占位func,修改了func几十次,依然不能劫持EIP。最后采用了劫持next_rcu的方式来劫持EIP。

为什么劫持next_rcu也可以控制EIP呢?在该结构体中, next_rcu构成了这个结构体链表,如果劫持了next_rcu,便可以控制它指向一个我们伪造好的mc_list。从而在释放时,执行inet->mc_list = iml->next_rcu。下次循环,就可以free这个我们控制的mc_list。由于没有SMAP,可以把这个mc_list布置在用户态。因此,我们便可以写一个循环来不停地控制func。引用一下thor的图:
Alt text

因此,我们堆喷射时,需要可以控制前8个字节,也就是next_rcu。除了可控数据之外,堆喷射还有大小的要求。linux 内核会把大小相近的堆块放在一起管理,堆块的大小一般都是2^N。ip_mc_socklist的大小是0x30,位于slab-64。可以通过申请该obj的反汇编代码看到,ip_mc_join_group中sock_kmalloc反汇编代码:Alt text
因此堆喷射时,kmalloc的大小要求为32byte<SIZE<=64byte。这样堆喷射时,内核会重用之前释放的内存。让我们堆喷射申请的内存占位原来的vulnerable obj释放的内存。

最终堆喷射的两个要求是:

寻找堆喷射的函数时,hardenedlinux中上传的exp中堆喷射的是调用了setsockopt(ser_sockfd, SOL_IP, MCAST_JOIN_GROUP, &req, sizeof req),搜索了一下相关实现,调用链为do_ipv6_setsockopt->ipv6_sock_mc_join->sock_kmalloc
Alt text
IDA找了一下spray的大小是0x48,不符合要求。
Alt text

thor的利用思路中提到,使用ip_mc_source函数来堆喷射。使用该函数来堆喷射时,可以控制前8个字节为0x10000000a,并且spray size大小是0x40,符合要求。
Alt text
通过understand寻找对于sock_kmalloc的引用,内核中也存在其它的函数可以使用:
Alt text
因此,利用ip_mc_source来堆喷射的代码为:

环境

关闭smep 和kaslr需要修改grub文件/etc/grub.d/40_custom中的kernel启动选项,增加nosmep nokaslr

劫持了EIP后,在没有kaslr和smep smap的保护下,利用还是比较简单的,ret2usr中的shellcode即可。注意由于劫持EIP时的thread并不是POC的thread,因此的传统的commit_creds(prepare_kernel_cred(0))并不能使用。利用的exploit如下,通过找到当前exp进程的kernel pid,找到对应的task_struct,再修改cred中的标志位提权。
Alt text

其中find_get_pid ,pid_task以及cred的偏移都和当前的kernel版本有关,需要自行修改。

对应汇编如下:

exploit地址
Alt text

SMEP是,位于CR4寄存器的第20位。开启了smep后,kernel就不难直接执行用户态的shellcode了。查看smep是否开启:cat /proc/cpuinfo | grep smep

运行之前的exp,内核oops信息如下:
Alt text

这里使用了内核rop的方式修改了CR4寄存器。

由于使用内核rop,因此需要迁移栈。
控制EIP时,rax是0x1000002a。因此栈迁移xchg eax, esp ; ret,测试了一下这条指令会修改esp,并且rsp的高位会清零。因此内核栈rsp=0x1000002a。

执行完用户态shellcode返回时,内核栈便遭到了破坏。因此,rop时我保存了ebp,在shellcode结束时leave,这样便可以恢复内核执行流程。leave相当于mov esp,ebp;ret。因此在rop结束时,只要ebp是正确的,leave指令就可以保证内核栈不会被破坏。

因此,最终的rop功能位为,把rbp 被保存到rcx中,并且修改了cr4,跳转到ret_addr 也就是用户态的shellcode处执行:

用户态shellcode需要把rcx中的值给rbp,执行完patch的功能后,leave恢复栈再退出。

此外,有几个注意点,大部分已经解决了:

gdb动态调试时,会存在其它的thread需要保存rcu_head到链表中,需要修改rcu_head链表中最后一个rcu_head中的next指针。如果最后一个rcu_head已经是在用户态伪造好的的rcu_head,就会出现问题。由于其它的thread不在exp上下文中,因此访问不了rcu_head末尾加入的在用户态的fake mc_list,地址为0x1000000a,直接运行exp就没这个问题了,只有调试时会遇到这个问题。Alt text

当执行用户态shellcode时,kgdb会挂掉,这时劫持kdump看内核oops信息,使用方法见之前的环境搭建文章。

当然还有一些没解决的:

下载:exploit地址
Alt text

最后,感谢SetRet和thor两位前辈的文章,当然还有一些其他帮助过我的前辈们。

 
 
 
 
 
 
 
 
 

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2019-1-29 12:25 被心许雪编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 123
活跃值: (1675)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
有大佬,心不慌
2019-1-29 15:15
0
雪    币: 3344
活跃值: (10982)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
3
等放假了要好好学习一下楼主的干货
2019-1-29 15:28
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
4
感谢分享!
2019-2-1 10:08
0
雪    币: 292
活跃值: (810)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
5
我在写windows kernel exp的时候也出现过修改内核list entry到fake obj的时候发生bsod(panic),原因就是楼主所说的其他的内核进程访问到fake obj,由于构造在userspace所以访问不到导致panic,至于为什么直接执行就不会出现,原因我觉得应该是时间窗太小,其他进程访问时这边exp的逻辑已经执行完毕了,楼主可以尝试让自己的exp thread睡上几秒钟,应该还会出现panic的情况
2019-2-12 10:40
1
雪    币: 4366
活跃值: (353)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
6
Keoyo 我在写windows kernel exp的时候也出现过修改内核list entry到fake obj的时候发生bsod(panic),原因就是楼主所说的其他的内核进程访问到fake obj,由于构造 ...
确实是这样的。
2019-2-13 14:55
0
雪    币: 177
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
请问出现socket:too many open files 怎么解决呐?
2019-3-27 09:56
0
雪    币: 4366
活跃值: (353)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
8
零零七Robot 请问出现socket:too many open files 怎么解决呐?
可以使用ulimit修改进程打开的socket的数量,或者修改一下相关的配置文件永久生效。
2019-3-27 11:00
0
雪    币: 177
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
请问 ip_mc_socklist的大小是0x30,位于slab-64?0x30是mov esi,0x30,那slab-64怎么计算的呢?
2019-5-14 11:19
0
雪    币: 4366
活跃值: (353)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
10
零零七Robot 请问 ip_mc_socklist的大小是0x30,位于slab-64?0x30是mov esi,0x30,那slab-64怎么计算的呢?
我的理解是,如果申请的内存大小 32<n<=64,就是位于slab-64啦。0x30是48byte。
内核内存管理算法可以参考一下这里:关于伙伴算法和slab,https://zhuanlan.zhihu.com/p/36140017。
2019-5-21 10:08
0
雪    币: 177
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
心许雪 我的理解是,如果申请的内存大小 32
谢谢
2019-6-8 15:04
0
雪    币: 300
活跃值: (2472)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
mark
2019-6-8 18:47
0
雪    币: 696
活跃值: (118)
能力值: ( LV6,RANK:141 )
在线值:
发帖
回帖
粉丝
13
请问您是怎么解决0x1000000a调试时访问错误这个问题的
2019-6-27 21:10
0
雪    币: 159
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
感谢。
最后于 2019-12-4 10:54 被xiaozhu头编辑 ,原因:
2019-12-3 20:53
0
游客
登录 | 注册 方可回帖
返回
//