最近在整理Linux内核漏洞利用的一些场景,比如利用系统调用漏洞、利用一些比较容易fuzz的漏洞,通过对这类场景的总结可以帮助我们快速发现0day,积累漏洞挖掘的经验。虽然这一类型的漏洞出现的频率很低但是优点也很显著,这类漏洞利用比较简单、直接。我通过查看github上Linux源代码patch日志发现CVE-2017-5123就是我要收集的目标,这类漏洞的攻击面是内核处理用户态指针出错,一般是内核没有检查用户态指针就直接使用,或者是对用户态指针检查不正确(本来是用来read的指针,却用来write)等, 一般会在access_ok、copy_from_user、copy_to_user 等几个函数使用中出现漏洞。
著名exploit网站Exploit Database对于CVE-2017-5123的介绍
链接地址:https://www.exploit-db.com/exploits/43029/
漏洞针对的Linux内核版本: Linux version 4.14.0-rc4+ (fuzz@ns388631) (gcc version 7.1.1 20170621 (GCC)) #1 SMP Mon Oct 16 21:54:35 CEST 2017
Android系统不受此漏洞影响。
0×1 测试环境
在virtualbox虚拟机ubuntu 14.04上测试。测试流程和视频中完全一样。
https://asciinema.org/a/BeRNWtrX27yF28CMeflqHQT0H
(exploit代码不是在Host系统中测试,通过qemu挂载有漏洞的kernel和filesystem 镜像测试exploit)
0×2 漏洞原理
漏洞出现在waitid系统调用中,因为没有检查用户态传入的指针的有效性导致用户态可以任意内核地址写漏洞。
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
patch是调用access_ok检查infop参数的有效性。
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=96ca579a1ecc943b75beba58bebb0356f6cc4b51
patch中添加了检查参数infop用户态指针是否可以写入的检测。如果infop指向无效地址, 或者内核地址空间都是非法的。
0×3 Exploit 分析
在github上有人提供了一份利用内核任意地址写漏洞本地提权的exploit,要执行此 exploit 必须关闭mmap_min_addr 和 SMEP 防护。
这份exploit 比较简单,学习的重点放在如何修改的内核内存达到劫持控制流。
首先exploit从内核符号表获取了commit_creds,prepare_kernel_cred函数的地址。 commit_creds(cred *)函数可以更新当前进程的cred凭据。prepare_kernel_cred()函数可以创建一个凭据。
接下来申请一片内存用来存储shellcode。这里从0地址开始映射了4096 byte内存。
这里先是向0xffffffff81f3f45a 地址写入0, 然后跳转到get_root 函数处去刷新当前进程的 凭据, 刷新凭据的代码使用通用的提权代码,如下:
上述权限提升有效载荷会分配一个新的凭证结构(uid=0, gid=0等)并将它应用到调用进程中, 当前这个exploit就是使用的这个payload达到提权的目的。因为关闭SMEP所以免去了用rop翻转smep绕过SMEP的限制。
最后该触发漏洞漏洞了,这里先调用waitid(1, pid,0xffffffff81f3f45a,…)修改内核地址0xffffffff81f3f45a的内容,然后调用fork函数。一般的套路是修改内核data段内一个全局结构体中的函数指针地址,然后在调用此函数指针跳转到我们的目标地址。
内核中有很多这样的结构,比如avc_callbacks、cgroup_subsys、have_fork_callback 等等。这里作者只给了一个地址没有说明是那个函数,需要动态调试内核到这个地址查看内容才能知道使用的是哪个函数指针。
目前受到网络影响没办法调试漏洞,暂且分析作者的调试日志,可以发现漏洞触发过程的调用栈。
Trace:
[ 6.371828] ? cgroup_can_fork+0×63/0xb0
[ 6.372082] copy_process.part.55+0x115f/0x19e0
[ 6.372328] _do_fork+0xbd/0×370
[ 6.372454] SyS_clone+0×14/0×20
[ 6.372560] do_syscall_64+0x4e/0×100
[ 6.372669] entry_SYSCALL64_slow_pat
根据调用栈 我们就知道是修改了fork函数实现中的某个函数指针,下面继续分析源代码。
(linux/kernel/cgroup/cgroup.c)
cgroup_subsys结构体数组是一个全局变量伴随内核启动初始化完毕,这里调用cgroup_subsys结构体中的can_fork函数指针,所以可以猜测 0xffffffff81f3f45a地址就在 cgroup_subsys对象附近。
再来看下waitid是如何写入0到0xffffffff81f3f45a地址。
waitid中多处调用unsafe_put_user()向infop指针(0xffffffff81f3f45a)写入数据。
exploit中调用完waitid紧接着调用了fork函数触发了can_fork函数跳转到0地址执行shellcode。
漏洞分析和exploit的原理分析完毕,我在本地做了测试非常稳定的exploit,感兴趣可以看看文章开头的提权视频。
0×4 exploit 代码说明
exploit 执行
exploit代码执行顺序:
1、run_kernel.sh在qemu中启动vul linux(开启靶场),从qemu 启动日志和脚本可以看到系统防护的状态(NX开启了,kaslr关闭了)
2、setup_exploit.sh在1执行完以后,当前的终端会进入vul linux系统,此时是root 用户。此脚本会关闭mmap_min_addr限制允许exploit mmap 0地址,并输出”./exploit 0xffffffff81f3f45a”。这里要启动一个普通用户,准备提升权限。
3、./exploit 0xffffffff81f3f45a在2中输出了这条命令,exploit是二进制程序没有源代码,可以逆向或者自己分析漏洞写exp。此exp已经具有任意内核地址写的能力。
编译kernel & filesystem方法
kernel和filesystem的编译方法在 kernel_compilation_cheatsheet.md文件中。
安装软件
0×5 测试截图:
exploit 代码:
https://github.com/nongiach/CVE/tree/master/CVE-2017-5123
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/54705.html