本来是想尝试在android下复现,但最后还是只在linux下复现成功了,可能还是出现了些纰漏的地方
以前搭建过linux的内核环境,当时是为了做kernel pwn搭建的,但是尝试复现android kernel的漏洞,虽说原理相同,但还是重新搭建了新的环境。
搭建环境的步骤基本没遇到什么大坑,跟着这个库 走基本就没遇到什么大坑
编译完成后,就会有两个主要的文件:goldfish/vmlinux 和 goldfish/arch/arm/boot/zImage。前面那个用于在调试时 gdb加载,后面的用于在安卓模拟器启动时加载。 vmlinux 用于提供符号表,zImage 则用于运行环境
然后下载或者编译sdk,下载完成后解压并将 android-sdk-linux/tools 加入环境变量(.bashrc)
还要下jdk 在终端中输入 android,下载我们需要的 SDK 和系统镜像
创建模拟器
其选项按需求选择,反正我一开始是一路enter的。。。
接下来进入 goldfish 目录,执行下面的命令用我们的内核运行模拟器,并在 1234 端口 起一个 gdbserver 方便内核调试。
再开一个 shell,进入 goldfish 目录,加载 vmlinux 以便调试内核:
这里的gdb注意因为前面export path了,所以实际路径是在arm-linux-androideabi-4.6/bin里
出现arm-linux-androideabi-gdb: error while loading shared libraries: libpython2.6.so.1.0: cannot open shared object file: No such file or directory问题时用以下方法即可:
这样基本上就可以调试内核了。。
整体来说不太复杂,就是拖文件够呛,网络太差了orz 之前调试kernel pwn题的时候还要烦些,后来用了两种方式搞定kernel环境,一是在虚拟机里搞定后再在本机的vscode 用ssh连接虚拟机,就实现了在vscode边写代码边测试的路子(很舒服),后来还是觉得麻烦因为要关hyper,不能使用wsl处理一般事务,于是就直接在我的pwn docker里直接搭建环境也成功了,然后再用docker共享文件也实现了边写代码边测试的路子(更舒服而且cpu的负荷啥的也小),具体搭建kernel环境的步骤不再累述,网上很多。
放一下一些内核的安全利用点(很不全),可以直接看ctf wiki或者其他的类似博客,大佬略过即可
KASLR 内核地址空间随机化。 内核地址显示限制 即kptr restrict指示是否限制通过/ proc和其他接口暴露内核地址。 0:默认情况下,没有任何限制。 1:使用%pK格式说明符打印的内核指针将被替换为0,除非用户具有CAP SYSLOG特权 2:使用%pK打印的内核指针将被替换为0而不管特权。
也就是说,当kptr_ restrict被限制的时候我们不能直接通过cat /proc/kallsyms来获得commit_creds的地址,要禁用该限制使用下面的命令: sudo sysctl -w kernel.kptr_restrict=0
内核的rop
smep smep位于CR4寄存器的第20位,设置为1。CR4寄存器的值:0x1407f0 = 0001 0100 0000 0111 1111 0000 关闭SMEP方法 修改/etc/default/grub文件中的GRUB_CMDLINE_LINUX="",加上nosmep/nosmap/nokaslr,然后update-grub就好
绕过smep的方法 由于我们只能在内核空间执行代码,但是不能把ROP链放到内核空间中,所以只能用stack pivot把ROP链放到用户空间。然后在内核空间找到合适的gadget放到ROP链中stack pivot
还有一种比较简单的绕过SMEP的方法是使用ROP翻转CR4的第20位并禁用SMEP,然后再执行commit_creds(prepare_kernel_cred(0))获取root权限。 如下构造
漏洞的问题点其实不难,做过一点pwn的师傅们都能看的出来是个OOB类型的漏洞,简要分析下 先看patch,发现增加了个对req->sdiag_family的大小检查,于是定位到这个函数。
发现外面还有个封装,不难看出要触发这个函数需要nlh的nlmsg_type类型为SOCK_DIAG_BY_FAMILY
可以在linux手册上 看到nlmsghdr结构
要想调用到该结构体,需要使用NETLINK_SOCK_DIAG协议的Netlink套接字发送消息,具体可参考netlink编程 或Netlink Socket ,这里就不再累述。
再看__sock_diag_rcv_msg函数,可以知道,如果没有patch,那么在sock_diag_lock_handler的参数我们可以调用超过AF_MAX大小的值
再看sock_diag_lock_handler以及sock_diag_handlers函数组的定义,发现在这里就出现了OOB的问题
根据上面的一些介绍和链接参考基本上就能知道要如何去构造而exp了, 我看网络上公布的exp大致原理相同,可参考1 ,2 ,3 ,而 AndroidKernelExploitationPlayground是自己实现了个类似的漏洞,大致原理原理类似,但无需构造socket去与他交互和填充具体的结构体数值,更加好理解些。 exp的分析一起写在代码里了
最后运行时需要注意,不要用串了。。。我一开始用错一个exp,直接用了exploit-db上的exp,而其对应的linux内核版本是3.3-3.8,而在Android模拟器上复现的话,还是用ndk编译,且具体的数据构造和exp内容(特别是汇编部分)还是不一样的,注意查看下对应的系统结构比如下面这个,注意对应的gcc版本,一开始没注意还运行不起来。。。
执行后程序报错,可能还是有些不准,pc值是0有些迷惑。。
未完待续吧,这个库里面还是有很多其他漏洞的,也推荐给想做arm上kernel漏洞复现的同学,但可能给android上的体会不太深,比较和Linux内核类似,且这次踩坑的时候心态有些炸了,具体的调试分析也直接草草分析过掉,希望下次能好些吧,争取复现下其他层次的漏洞。 唉,漫漫长路啊。。。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)