内核漏洞学习记录(CVE-2021-22555)

内核漏洞学习记录(CVE-2021-22555)

pureGavin 看雪学苑 2024-08-03 17:59

看雪的二进制课程已经学习结束了,此篇是考核内容,顺便检测一下我对课程内容的理解程度。

参考内容

https://www.anquanke.com/post/id/254027

https://arttnba3.cn/2022/04/01/CVE-0X07-CVE-2021-22555/

https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html

以及看雪的二进制课程。

先看一下kasan给出的信息。

看样子是xt_compat_target_from_user函数出现了两字节写的堆溢出。

由于本人对Linux内核不太熟悉,所以只能从exp入手来分析漏洞成因,以及漏洞所在的模块的大致作用。

poc如下:

编译命令如下:

对于exp的解读以及漏洞成因的分析

此处申请了4096个消息队列主要是为了堆喷的稳定和利用的稳定(后面会提),一般来说,堆喷的利用文章都会提到喷得越多,利用越稳定,可是很少提及为什么越多越稳定,此处我认为可以引用
CTFwiki(https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/heap/slub/uaf/#_1)
上的介绍内容,内核代码很有可能在不同CPU核心上运行,而
slub算法(https://ctf-wiki.org/pwn/linux/kernel-mode/exploitation/heap/heap_overview/#slab-allocator)
在不同核心上分配的内存也是不一样的,假设喷得不够多的情况下,exp在堆喷的时候用的是CPU0,而后面利用时却跑到了CPU1,就会导致利用失败。

main函数开头的这一段也是为了将进程绑定到固定核心上,以提高堆喷稳定性。

接下来需要创建对应的主消息和辅助消息并向其中填充数据。

mtext[0]=MSG_TAG用于标识某段区域的作用是否为堆喷

mtext[4]=i用于标识内存区id

此时的消息队列排列应该如下图所示:

此处采用了
官方文档(https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html)

图片
(主要是我找不到好的画图软件)。

紧接着是释放部分主消息,事实上只释放了三个主消息,1024、2048、3072。

调用setsockopt(IPT_SO_SET_REPLACE)会直接触发两字节溢出写0,之前连续申请了4096个消息队列也是为了能让消息队列所在的内存是连续的,其实最重要的是能让辅助消息队列所在的内存是连续的,因为触发的是两字节写0,也就是说主消息对应的辅助消息如果原本内存地址不为0的情况下,触发漏洞过后,会有两个主消息指向同一个辅助消息,也就是从上面的图变成了下图:

有些
教程(https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html)
认为在触发漏洞时,中间还有个如下图所示的状态:

从漏洞原理以及触发条件来看,我认为这种状态并不需要特别注意,在调用setsockopt函数时,内核中确实是先创建了xt_table_info,然后在八字节对齐时产生两字节溢出,但是从用户态看来,调用完setsockopt函数后就直接发生了溢出;总之,这种中间状态存在时间非常短,可以忽略不计,但对理解漏洞形成流程还是有帮助的。

至此,我们已经触发了这个漏洞,根据之前所做的内容来看,触发漏洞更需要几个条件;首先需要将代码编译成32位程序,并且在64位系统上运行;其次需要编译x_tables和ip6_tables这两个内核模块,最后需要在调用setsockopt时加上一些特别的字段;32位系统调用setsockopt(IPT_SO_SET_REPLACE)函数最终会走到内核的 __compat_sys_setsockopt 函数中,这点也可以从之前kasan的输出信息中看到。

关于setsockopt的
内核调用流程(https://arttnba3.cn/2022/04/01/CVE-0X07-CVE-2021-22555/#32-%E4%BD%8D%E4%B8%8B%E7%9A%84-setsockopt-%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8)
十分复杂,且与漏洞原理本身无关(适当的了解还是有必要的,可以加深对Linux内核的理解),所以就直接从漏洞点来分析。

漏洞点实际是从compat_do_replace函数开始便存在了,并在xt_compat_target_from_user函数中被触发。

先看一下compat_do_replace函数
代码。(https://elixir.bootlin.com/linux/v5.8.1/source/net/ipv6/netfilter/ip6_tables.c#L1498)

从上面trigger_oob_write函数中的漏洞触发代码来看,struct compat_ip6t_replace tmp 对应着 struct ip6t_replace replace。

注意此处的kvmalloc,这与漏洞产生原因有关,后面会提到。

而newinfo则是xt_table_info头加如下一堆结构体。

最后在调用translate_compat_table函数时给的&loc_cpu_entry指针则是指向xt_table_info结构体里的可变长度结构体entries。

简单的形容translate_compat_table函数的作用就是将32位的结构体传到entry0中,然后申请一个和entry0同样大小的entry1  64位结构体,然后将entry0赋值给entry1(注意,此时entry0中的内容不做修改,只是从32位转到了64位而已),到此为止KASAN的信息就不全了,此处的调用链+应该是 translate_compat_table -> compat_copy_entry_from_user -> xt_compat_target_from_user,中间的函数不怎么重要,主要就是参数传递的过程,重要的是xt_compat_target_from_user函数。

此处需要注意pad变量,在赋值时,代码作者想要进行8字节对齐,但是在上面xt_alloc_table_info函数在申请内存时,并没有进行8字节对齐,这意味着在memset置零的时候可能会将0写到下一个堆块中(溢出后产生的效果可以参考上面的图片)。

此时漏洞形成的大致原理就已经清楚了,在64位系统中执行32位的含有socket的代码(漏洞触发代码参考trigger_oob_write函数),所以要想通过syzkaller来检测到此漏洞,就需要使用64位的内核来运行32位的样本。

模糊测试

系统配置

需要开启CPU虚拟化

8G内存(内存需要尽可能大)

40G硬盘(看网上说的,应该够用)

前置工作

安装一些依赖包

根据
GitHub(https://github.com/google/syzkaller/issues/760)
上给出的错误修改提示和一些必须的配置修改.config文件。

还有一些代码需要修改。

将/include/linux/compiler.h头文件第392行注释掉,这个错误不知道为什么会被触发,我看了更新版本的内核,这行还是在的,可能是我机器的玄学问题。

修改/tools/objtool/elf.c中第356行的if判断,直接改为return 0,这个问题是根据
GitHub(https://github.com/torvalds/linux/commit/1d489151e9f9d1647110277ff77282fe4d96d09b.patch)
上的patch代码修改的,事实上新版本的内核也直接改为return 0了。

以上需要修改的源码内容是在Ubuntu22.04上编译5.8.1内核的时候必要的修改,如果使用的是20.04则不需要对源码做修改,只需要修改.config文件就可以了。

然后需要修改一下syzkaller的源码内容。

此处需要找到qemu.go文件,将如下内容:

修改为:

修改完成后需要重新编译syzkaller,编译完成后直接运行就好。

运行之前需要写一些syzkaller的配置文件,并下载镜像文件。

这里target给的是386是因为之前修改了syzkaller源码,所以实际运行的是64位的系统,只不过syzkaller喂的是32位的样本。

下面的目录都是根据实际工作目录填写;count、cpu和mem参数,则分别代表虚拟机数、CPU数和内存大小,这些根据实际物理机配置需要尽可能大一些。

之后就可以使用
syz-manager(https://github.com/google/syzkaller/blob/master/docs/usage.md#running)
开始fuzz了,这里的config文件则是上面自己修改的config文件。

第一次使用syzkaller可能会有些疑惑,为什么运行一段时间后所有的虚拟机都停了,这是因为syzkaller每隔一段时间就需要验证跑出来的crash,并生成C代码。

总之fuzz时,耐心等待即可。

其实fuzz部分文章去年十一月就差不多完成了,但是因为工作变故,导致拖延了很久,总之最后能发出来就是好的,另外漏洞本身除了fuzz部分还有利用部分,所以此文章应该还有一篇,等有空了就给大家鸽出来吧。

看雪ID:pureGavin

https://bbs.kanxue.com/user-home-777502.htm

*本文为看雪论坛精华文章,由 pureGavin 原创,转载请注明来自看雪社区

# 往期推荐

1、Alt-Tab Terminator注册算法逆向

2、恶意木马历险记

3、VMP源码分析:反调试与绕过方法

4、Chrome V8 issue 1486342浅析

5、Cython逆向-语言特性分析

球分享

球点赞

球在看

点击阅读原文查看更多