为什么是结构体 msg_msg:
结构体长度可以由用户空间决定
首先, msg_msg 结构体的长度可以由用户空间间接地控制,这意味着 msg 可以落在指定类型的 cache 上。任何大小从 sizeof(struct msg_msg) 到 PAGE_SIZE 的 object 存在 UAF/double free 的漏洞时,msg_msg 都可以申请到该 cache 上实现堆喷漏洞利用。下面是 msgsnd() 的原型:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgsz 将传递给 alloc_msg, 并最终使用 kmalloc 分配到相应的 cache 上:
static struct msg_msg *alloc_msg(size_t len)
{
struct msg_msg *msg;
…
size_t alen;
alen = min(len, DATALEN_MSG);
msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL_ACCOUNT); // alloc msg
if (msg == NULL)
return NULL;
…
len -= alen;
…
while (len > 0) {
struct msg_msgseg *seg;
…
alen = min(len, DATALEN_SEG);
seg = kmalloc(sizeof(*seg) + alen, GFP_KERNEL_ACCOUNT); // alloc msg_seg
if (seg == NULL)
goto out_err;
…
}
return msg;
…
}
因此在漏洞利用中,很容易通过精心计算 msg_msg 的长度,使其落在存在 UAF/double free 漏洞的 object 附近, 进一步完成堆喷和内存污染。
实现任意读
由于内核会将 msgsnd() 传入的缓存切割成较短的段,并且用指针来连结成链表:
struct msg_msg {
struct list_head m_list;
long m_type;
size_t
; /* message text size */
struct msg_msgseg *next;
void *security;
/* the actual message follows immediately */
};
struct msg_msgseg {
struct msg_msgseg *next;
/* the next part of the message follows immediately */
};
为了读取 msg 的数据缓存, load_msg 实现了这样一个循环来读取每一个切割成的段:
struct msg_msg *load_msg(const void __user *src, size_t len)
{
…
if (copy_from_user(msg + 1, src, alen))
goto out_err;
/* read segments one by one */
for (seg = msg->next; seg != NULL; seg = seg->next) {
len -= alen;
src = (char __user *)src + alen;
alen = min(len, DATALEN_SEG);
if (copy_from_user(seg + 1, src, alen))
goto out_err;
}
…
return msg;
…
}
缓存的长度并不确定, 读取大小完全决定于 size_t m_ts, 一旦size_t m_ts 和 struct msg_msgseg *next” 被污染, 用户空间使用 msgrcv() 就能实现任意读。甚至只污染 size_t m_ts` 的情形下,也能实现越界读取。这意味着被污染的 msg 可以用来做信息泄漏。
这里有一些使用 msg_msg 达成信息泄漏的例子:Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel – Achieving arbitrary read: overwrite next and size_t m_ts achieve arbitrary read.
CVE-2021-22555: Turning x00x00 into 10000$ – Exploring struct msg_msg: out-of-bound read.
Linux Kernel Exploit Development: 1day case study: out-of-bound read.
可被用来实现任意 free
以下这些例子,展现如何在使用已有的代码路径和被污染的 msg 达成任意free:
Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel – Four bytes of power
CVE-2021-22555: Turning x00x00 into 10000$ – Achieving a better use-after-free: 注意这个例子释放 msg 的代码路径是内核原有用于释放 msg 的代码路径。
任意释放已经非常接近完成提权,因为他能够实现在任意的结构体的 UAF, 比如一个带有函数指针的结构体被污染以后,就能实现控制流劫持来完成ROP。更直接的是,最近有许多研究人员发现直接对 cred_jar cache 的堆喷能够实现提权。 使用任意 free 来释放当前进程的 cred 指针,在用户空间触发新的高权限进程,比如:sshd, su, sudo, 其中的某个 cred object 会落在当前进程 cred 指针所指的位置,这样提权就已经完成了。
关于 DirtyCred 的一些故事
cred_jar 堆喷现在被一些研究人员叫做 “DirtyCred”, 其作者声称是他们发现的新方法,实际上则是已经被使用并且在此之前被多个研究人员公开发布。我们曾经揭露过该作者如何收集 Syzkaller 社区和 HardenedLinux 社区的 Harbian-QA 项目的信息,重新实现或者重现这些内容,重命名他们,然后撰写论文声称是他们自己独立发明的,并且在论文引用上分毫不提对社区经验的借鉴。同样的情况, DirtyCred 也是类似的, 可以知道这种方法已经被大量的研究人员使用
时间表:
Mar 19, 2020: xfrm_poc_RE_challenge Vitaly Nikolenko
Mar 1, 2022: Exploiting CVE-2021-26708 (Linux kernel) with sshd by HardenedVault
Aug 13, 2022: HardenedLinux’s tweet
Sep 8, 2022: Vitaly Nikolenko’s tweet
VED 一些不完全的缓解措施
msg_msg 的完整性检查
VED 维护了一个 msg_msg 的列表,计算每个 msg_msg 内容的 hash。 使用 msgrcv() 读取或释放struct msg_msgseg next, size_t m_ts 和 void *security’ 被污染的 msg_msg 将会被 VED 探测到。但是我们的检查并没有包含 stuct list_head m_list`, 这意味着如果该指针被污染并被释放,VED 并不能检查出来,因为他是由 msg_queue 来维护的。CVE-2021-22555: Turning x00x00 into 10000$ – Exploring struct msg_msg 就是这样一个例子. 但 VED 使用其他办法来进行防御。
越界读检查
VED 添加了越界读取的检查。如果说读取目标缓存的长度和读取长度是不匹配的, VED 就能检查到污染。在漏洞利用中’size_t m_ts’的污染较容易达成越界读取, 并且实现 KASLR 绕过或者泄漏堆地址。VED 的检查能够有效检查到越界读取, 但是这个防御也是不完整的,精心制作的 msg 仍可能绕过。比如说, 存在 UAF/double free 漏洞的结构体,msg_msg 结构体, 需要泄漏目标结构体, 三者的长度都是一致的,仍可以绕过 VED 的检查。
这两个缓解措施均是可被绕过的,VED 目前的版本是基于 LKRG 实现的,检查的完整性与性能的平衡是需要考虑的。更加严密的 msg 是可能的,但是 kprobe 的检查点数量,计算量需要多的多。另外则是虽然这两个缓解措施均可被绕过,但是叠加两者,由于其中的检查是相互交叉的,比方说完整性检查包含的 struct msg_msgseg *next, size_t m_ts,和越界检查。这使得漏洞利用需要依赖于原代码路径来实现堆喷,和更依赖同类型的 object 之间的污染,使得 msg 的漏洞利用困难程度上升。当然这些专门的绕过对资深的内核黑客来说不会是多大的问题。VED 也在探索更加完整并且平衡性能损耗的方案。
效果
由于目前的漏洞利用并不会针对上述防御进行绕过,VED针对公开的 exploit,仍然能够轻松检查出来,尤其是在利用较早期的infoleak阶段就能达成防御,这是我们测试过的一些漏洞 POC:
1:CVE-2022-25636: 完整性校验.
2:CVE-2021-26708: 完整性校验.
3:CVE-2021-22555:越界读检查
4:CVE-2021-22555: 越界读检查
Demo
公众号输入:spray,后即可查看demo
HardenedVault:
https://hardenedvault.net
Github:
https://github.com/hardenedvault
HardenedVault
https://hardenedvault.net/
原文始发于微信公众号(赛博堡垒):漏洞利用幽灵'msg_msg'以及VED的防护策略