漏洞利用幽灵'msg_msg'以及VED的防护策略
2022-11-13 18:52:48 Author: 赛博堡垒(查看原文) 阅读量:12 收藏

为什么是结构体 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 \x00\x00 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 \x00\x00 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 \x00\x00 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/


文章来源: http://mp.weixin.qq.com/s?__biz=MzkxMTI4MDI3NQ==&mid=2247484022&idx=1&sn=93349f7d407b0ca2a778369a699e6a28&chksm=c11fd38af6685a9c08bb524798ecae2db25bf0b0913e92620b26ec677874ba72a1095a66f545#rd
如有侵权请联系:admin#unsafe.sh