!
也想出现在这里? 联系我们
广告位
当前位置:首页>安全咨询>漏洞复现>watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

介绍

在我第一次研究 Linux BleedingTooth之后,我也想找到一个提权漏洞。我首先查看了 CVE-2016-3134 CVE-2016-4997 等旧漏洞,这些漏洞启发了我在 Netfilter 代码中memcpy()使用grepmemset()这导致我发现了一些错误的代码。
 


 

漏洞

IPT_SO_SET_REPLACEIP6T_SO_SET_REPLACE在兼容模式下调用时,需要CAP_NET_ADMIN在用户+网络命名空间中获得的能力,结构需要从用户转换为内核以及从 32 位转换为 64 位,以便由本机函数处理。自然,这注定容易出错。我们的漏洞在xt_compat_target_from_user()wherememset()被调用时使用了target->targetsize在分配期间没有考虑到的偏移量 - 导致一些字节越界写入:



targetsize不是由用户控制的,但可以通过名称(如,或)选择具有不同结构大小的TCPMSS不同TTL目标NFQUEUE。越大targetsize,我们可以改变的偏移量越大。但是,目标大小不得为 8 个字节对齐以实现pad > 0. 我发现最大的可能是NFLOG我们可以选择一个最大为 0x4C 字节的偏移量(可以通过在struct xt_entry_match和之间添加填充来影响偏移量struct xt_entry_target):



请注意,缓冲区的目标是分配的,GFP_KERNEL_ACCOUNT并且大小也可以不同:



不过,最小大小大于 0x100,这意味着可以分配此对象的最小平板是 kmalloc-512。换句话说,我们必须找到分配在 kmalloc-512 kmalloc-8192 之间的受害者来利用。
 

探索结构 msg_msg

使用 发送数据时msgsnd(),有效负载分为多个段:



struct msg_msg和的标题在哪里struct msg_msgseg



第一个成员struct msg_msgmlist.next指向队列中另一条消息的指针(不同于next因为这是指向下一个段的指针)。正如您接下来将学习的那样,这是一个完美的腐败候选人。
 


 

漏洞分析

首先,我们使用msgget(). struct msg_msg然后,我们使用 为每个消息队列发送一条大小为 4096(包括标头)的消息msgsnd(),我们将其称为主消息。最终,在大量消息之后,我们有一些是连续的:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

接下来,我们使用以下命令为每个消息队列发送大小为 1024的辅助消息msgsnd()

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

最后,我们在主消息中创建一些漏洞(在我们的例子中每 1024 个),并触发易受攻击的setsockopt(IPT_SO_SET_REPLACE)选项,在最好的情况下,它将struct xt_table_info在其中一个漏洞中分配对象:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

我们选择用零覆盖相邻对象的两个字节。假设我们与另一个主要消息相邻,我们覆盖的这些字节是指向辅助消息的指针的一部分。由于我们分配它们的大小为 1024 字节,因此我们有 1 - (1024 / 65536) 的机会重定向指针(我们失败的唯一情况是指针的两个最低有效字节已经为零)。

现在,我们可以期待的最好情况是被操纵的指针也指向一个次要消息,因为结果将是两个不同的主要消息指向同一个次要消息,这可能导致释放后使用:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

但是,我们如何知道哪两个主要消息指向同一个辅助消息?为了回答这个问题,我们用在 [0, 4096) 中的消息队列的索引标记每条(主要和次要)消息。然后,在触发损坏后,我们遍历所有消息队列,使用msgrcv()with查看所有消息MSG_COPY并查看它们是否相同。如果主消息的标签与辅助消息的标签不同,则表示它已被重定向。在这种情况下,主消息的标签代表虚假消息队列的索引,即包含错误辅助消息的队列。,而错误次要消息的标签代表真实消息队列的索引。知道了这两个索引,实现 use-after-free 现在是微不足道的 - 我们即使用从真正的消息队列中获取辅助消息并因此释放它:msgrcv()

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

请注意,我们仍然在假消息队列中引用了已释放的消息。
 

绕过 SMAP

使用 unix 套接字(可以通过 轻松设置socketpair()),我们现在喷射大量大小为 1024 的消息并模仿struct msg_msg标头。理想情况下,我们能够回收先前释放的消息的地址:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

请注意,它mlist.next41414141,因为我们还不知道任何内核地址(启用 SMAP 时,我们无法指定用户地址)。没有内核地址是至关重要的,因为它实际上会阻止我们再次释放块(稍后您将了解为什么需要这样做)。原因是在 期间msgrcv(),消息从循环列表的消息队列中取消链接。幸运的是,我们实际上处于实现信息泄露的有利位置,因为struct msg_msg. 即,该字段m_ts用于确定返回用户空间的数据量:



消息的原始大小只有1024-sizeof(struct msg_msg)字节,我们现在可以人为地增加到DATALEN_MSG=4096-sizeof(struct msg_msg). 因此,我们现在可以读取超出预期的消息大小并泄漏struct msg_msg相邻消息的标头。如前所述,消息队列被实现为一个循环列表,因此,mlist.next指向主消息。

知道主消息struct msg_msg的地址后,我们可以使用该地址重新制作假消息next(意味着它是下一个段)。然后可以通过读取多个字节来泄漏主要消息的内容。来自主消息DATALEN_MSG的泄露mlist.next指针揭示了与我们的假消息相邻的辅助消息的地址。从该地址中减去 1024,我们最终得到了假消息的地址。struct msg_msg
 

实现更好的免费使用后

struct msg_msg现在,我们可以用泄漏的地址重建假对象mlist.nextmlist.prev(意味着它指向自己),使假消息能够与假消息队列一起释放。

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

请注意,当使用 unix 套接字进行喷射时,我们实际上有一个struct sk_buff指向假消息的对象。显然,这意味着当我们释放假消息时,我们仍然有一个过时的引用:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

这个陈旧struct sk_buff的数据缓冲区是一个更好的使用后释放场景,因为它不包含标题信息,这意味着我们现在可以使用它来释放平板上的任何类型的对象。相比之下,struct msg_msg只有当前两个成员是可写指针(需要取消链接消息)时,才能释放对象。
 

寻找受害者对象

攻击的最佳受害者是在其结构中具有函数指针的人。请记住,还必须为受害者分配GFP_KERNEL_ACCOUNT.

在与 Jann Horn 交谈时,他提出了struct pipe_bufferkmalloc-1024 中分配的对象(因此为什么辅助消息是 1024 字节)。struct pipe_buffer可以很容易地用它pipe()作为alloc_pipe_info()子例程进行分配:



虽然它不直接包含函数指针,但它包含一个指向struct pipe_buf_operations另一方面具有函数指针的指针:


 

绕过 KASLR/SMEP

当一个人写入管道时,struct pipe_buffer被填充。最重要的是,ops将指向anon_pipe_buf_ops位于 .data 段中的静态结构:



由于 .data 段和 .text 段之间的差异总是相同的,因此anon_pipe_buf_ops基本上可以让我们计算内核基址。

我们喷射大量struct pipe_buffer对象并回收陈旧struct sk_buff数据缓冲区的位置:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

由于我们仍然有来自 的引用struct sk_buff,我们可以读取它的数据缓冲区,泄露 的内容struct pipe_buffer并显示 的地址anon_pipe_buf_ops



有了这些信息,我们现在可以找到 JOP/ROP 小工具。请注意,当从 unix 套接字读取时,我们实际上也释放了它的缓冲区:

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

 

提升权限

struct pipe_buffer我们用一个假的来回收陈旧的,ops指向一个假的struct pipe_buf_operations。因为我们知道它的地址,所以这个假结构被种植在同一个位置,显然,这个结构应该包含一个恶意函数指针release

watch_queue Linux 内核组件堆越界写入利用CVE-2022-0995

漏洞利用的最后阶段是关闭所有管道以触发释放,这反过来将启动 JOP 链。找到 JOP gadgets 很困难,因此目标是尽快实现内核堆栈枢轴,以便执行内核 ROP 链。
 

内核ROP

我们将 RBP 的值保存在内核中的某个暂存器地址,以便稍后恢复执行,然后调用commit_creds(prepare_kernel_cred(NULL))安装内核凭据,最后调用switch_task_namespaces(find_task_by_vpid(1), init_nsproxy)将进程 1 的命名空间切换到init进程之一。之后,我们恢复 RBP 的值并返回以恢复执行(将立即free_pipe_info()返回)。
 

转义容器并弹出根外壳

回到用户区,我们现在拥有更改 mntpid net 命名空间的 root 权限,以逃离容器并脱离 kubernetes pod。最终,我们弹出一个 root shell



 

漏洞复现

漏洞证明可在https://github.com/google/security-research/tree/master/pocs/linux/cve-2021-22555获得。

在易受攻击的机器上执行它会授予你 root 权限:



 

给TA打赏
共{{data.count}}人
人已打赏
漏洞复现

OLive存在任意文件读取漏洞

2022-10-11 22:08:16

漏洞复现

Atlassian Jira 多款产品Mobile Plugin服务端请求伪造漏洞CVE-2022-26135

2022-10-11 22:08:56

声明 本站上的部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。无意侵害您的权益,请发送邮件至 2651636361@qq.com 或点击右侧 私信:少羽 反馈,我们将尽快处理。
{{yiyan[0].hitokoto}}
0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索