如果存在use after free,就可以利用这些结构体进行提权。
我们来看一下内核中关于tty的源码
将全局变量ptm_drivere de ops 设置成ptm_unix98_ops,
而ptm_unix98_ops是在之前就已经初始化了的一个静态全局变量。
打开一个ptmx时调用的函数:
当打开一个ptmx时候调用这个函数之后利用ptm_driver来初始化。因此当我们可以利用 use after free控制一个tty_struct的时候,ops的位置的是一个全局内核变量ptm_unix98_ops,这个变量可以在kallsyms中读取,这样可以leak kernel 绕过kaslr。
需要申请一个0x1000的fake_file_operations 还需要申请一个fake_tty_operations,之后去伪造其中的ioctl 为xchgeaxesp 具体可以看以前发的那篇kernel的ctf题目。
题目举例:sharif kdb 2018国赛 babydriver
cred结构相信大家是轻车熟路,
通过一个超级粗暴的cred爆破代码带大家看看cred的利用
当我们有了内存的任意读写权限之后
但是这种爆破需要时间太长了
前几天看到一篇文章 http://reverse.put.as/2017/11/07/exploiting-cve-2017-5123/
利用cve-2017-5123waitid的漏洞 先进行内存堆探测, cve-2017-5123 最最最基础利用可以看我前面的文章, https://bbs.pediy.com/thread-247014.htm
感觉有必要做一个关于cve-2017-5123的专题,一个很适合练手的cve。
内核堆探测的代码:
waitid去任意写0,触发缺页异常并不会直接触发OOPS,但是会返回fault,这样就可以进行内存堆探测,只要能找到堆的大体位置,并且在上面通过fork,在堆上面分配大量cred结构,这样大大增加命中的几率。但是这里有一个困扰我多年的问题,那就是我找不到办法直接去调试kernel heap,希望大佬们能分享一些实用的工具。而且文章中给出的探测堆地址的代码中,为什么要以0x10000000为单位进行探测我始终不明白。准备接下来一段时间认真读一下linux kernel heap的源码,希望能有所解答。
vma的利用我都发在了这篇文章上,不再重复了,大家可以复现一波,这个cve真的很有意思。 https://www.anquanke.com/post/id/161632
vdso是以页为单位copy到内核地址空间的。如果可以内核地址任意的读,可以页为单位进行爆破vdso的地址。
这是String IPC的例子,首先找到有ELF头的一页,再看这页的偏移0x270的地方对不对。具体的情况可能这个偏移不一样,可以自行dump vdso找偏移进行测试。 有了vdso的地址,我们就可以进行overwrites
vdso overwrite技术
强网杯solid_core里面用到的技术: 劫持prctl中的hook 32位参数不能直接劫持call_usermoderhelper,但是可以先劫持poweroff_cmd这个全局变量,为我们想要执行的命令。需要绕过kaslr。kaslr的seed为0x100000,爆破出poweroff_cmd.这里也是一种绕过kaslr的爆破技术,在通过在ida中找出data段poweroff_cmd的偏移,以seed为单位进行爆破。其实跟通过爆破vdso找出kernel base是一个道理。之后将prctl劫持为orderly_poweroff。这里不详细讲了,可以看出题人大佬的博客。
http://simp1e.leanote.com/post/%E5%BC%BA%E7%BD%91%E6%9D%AF%E5%87%BA%E9%A2%98%E6%80%9D%E8%B7%AF-solid_core
插一句,这个题我用gdb调试的时候,题目给的vmlinux没有debug symbols table,难受的一匹,我只能先在qemu中读出kallsyms 中prctl的地址,然后手动的下段点。之后找hook也是难受的一匹。不知道大佬们有什么好用的办法。
中规中矩的一种方法,当有了kernelbase之后,构造一个内核rop组成的payload,write cr4,但是这个需要一些辅助的利用条件,比如存在ufa,就可以利用tty_struct绕过smep。可以参考之前tty_struct的介绍。
因为使用gdb远程调试内核,所以我一直没找到什么有效的手段查看内核的内存映射。一般就是通过readelf -a vmlinux看看text bss data段的偏移,在kallsyms中看看text段符号表,有些全局变量在ida中找找偏移。但是内核的堆和栈,没有像用户态看起来那么方便。
在这里说一说自己调试的时候是怎么处理的。希望大佬们能跟我分享一下自己的调试技巧。每次做kernel ctf pwn都烦skr人,然后用一些lowB的方法,唉!
对于栈的话没什么, grep 0 /sys/module/yourmodule/sections/.text 从sys/module中找出text段位置,之后 add-symbol-file ./yourmodule.ko 【address】 加载你的驱动的符号表,之后下断点,x/60gx $rsp就可以看栈的情况。
但是堆的话比较难受,原因是对于内核堆的分配算法 slub slab 伙伴算法,我理解不深,也没有实际去调试过这种题型。做过的ctf题目都是有一个简单的uaf,释放一个堆块,马上将其再申请出来这种。在网上也找不到什么实用工具。
所以我写了一个很简单的小驱动,kmalloc 一块地址,因为slub分配方法是 先通过伙伴系统获取一整页大小的虚拟内存,之后将整页分配成固定大小的内存块,根据kmalloc进行堆分配。所以,我想的是包装一个内核的模块,当我们要调试一个kernel的程序的时候,比如,需要申请一块ttystruct,kmalloc(0x2e0),这个时候,我们先在包装的内核模块中kmalloc一块同样大小的地址,将这个地址输出,这样,我们要调试的这块tty_struct就会跟我们内核模块输出的地址在同一页中,方便我们去查看堆。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2018-11-24 01:51
被obfuscation编辑
,原因: