议题解读:How I use a novel approach to exploit a limited OOB on Ubuntu at Pwn2Own Vancouver 2024

议题介绍了一种新颖的 Linux 内核栈缓冲区越界写漏洞利用技巧,结合了内核栈分配机制和 ebpf 实现任意地址写修改 modprobe_path 完成利用。 漏洞分析 漏洞代码: nla_parse_nested 解析用户态...

议题介绍了一种新颖的 Linux 内核栈缓冲区越界写漏洞利用技巧,结合了内核栈分配机制和 ebpf 实现任意地址写修改 modprobe_path 完成利用。

漏洞分析

漏洞代码:

image.png

nla_parse_nested 解析用户态下发的数据,然后取出 TCA_TAPRIO_TC_ENTRY_INDEX​ 放到 tc 变量中

  1. struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = {
  2. [TCA_TAPRIO_TC_ENTRY_INDEX] = { .type = NLA_U32 },
  3. [TCA_TAPRIO_TC_ENTRY_MAX_SDU] = { .type = NLA_U32 },
  4. [TCA_TAPRIO_TC_ENTRY_FP] = NLA_POLICY_RANGE(NLA_U32,
  5. TC_FP_EXPRESS,
  6. TC_FP_PREEMPTIBLE),
  7. };

由于 nla_get_u32 获取 TCA_TAPRIO_TC_ENTRY_INDEX 返回的类型为 u32,而 tc 变量是 int 类型,通过提供很大 TCA_TAPRIO_TC_ENTRY_INDEX 字段可以让 tc 变成负数,绕过后面的 tc 范围检查,写入 max_sdu 和 fp 数组

  1. u32 max_sdu[TC_QOPT_MAX_QUEUE],
  2. u32 fp[TC_QOPT_MAX_QUEUE],

跟踪调用路径 max_sdu 和 fp 为内核栈数组,通过负数的 tc ,可以向前越界写栈内存

image.png

漏洞的 Patch 比较简单:在 taprio_tc_policy 定义 TCA_TAPRIO_TC_ENTRY_INDEX​ 字段的最大值

  1. struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = {
  2. [TCA_TAPRIO_TC_ENTRY_INDEX] = NLA_POLICY_MAX(NLA_U32,
  3. TC_QOPT_MAX_QUEUE),
  4. [TCA_TAPRIO_TC_ENTRY_MAX_SDU] = { .type = NLA_U32 },
  5. [TCA_TAPRIO_TC_ENTRY_FP] = NLA_POLICY_RANGE(NLA_U32,
  6. TC_FP_EXPRESS,
  7. TC_FP_PREEMPTIBLE),
  8. };

漏洞利用

漏洞利用前需要观察漏洞代码,确定漏洞的原语:

image.png

  • 写入的大小为 u32
  • max_sdu 写的值需要 小于 dev->max_mtu,通过控制 val 的最大值为 65535(0xffff)
  • fp 写的值受到 taprio_tc_policy 的约束,值只能为 1 和 2

可以看到漏洞原语非常受限,最大只能写 0xffff,而且每次都是 u32 写,就没有办法部分修改栈上面的指针(改完后偏移太大很难控制,比如 0xffffffaabbccdd —> 0xffffff0000xxxx)

image.png

图中描述了内核栈布局, max_sdu 在调用者栈帧,通过向前越界写可以篡改 taprio_parse_tc_entry 函数栈帧中的数据,其中有用的数据就是一些指针,如果利用原语篡改,大概率得到的是非法指针,无法使用。

常规的栈越界写漏洞利用策略行不通后,可以考虑结合目标的情况,这里就用到了内核的实现机制,Linux 内核的进程栈其实是在 VMALLOC 区域中分配的,而内核的 vmalloc 接口也是在这个区域中分配内存,因此不同进程的 栈内存、vmalloc 堆内存都是相邻的。

image.png

在 VMALLOC​ 区域分配内存时,如果没有指定 NO_GUARD_PAGE​ 参数就会在分配的内存后面加上一个不可访问的页作为 grard page​,防止溢出到相邻块,不过我们是 OOB 所以不受影响

下面顺便介绍下 VMALLOC​ 区域的内存分配/释放机制,以及内存布局的方式。在 Linux 内核的虚拟地址空间中由 [VMALLOC_START, VMALLOC_END]​ 组成的区域称为 VMALLOC​ 区域,用于分配虚拟地址连续但是物理地址不一定连续的内存, VMALLOC​ 区域中已经分配出去的虚拟地址空间通过 vm_area​ 结构体表示,如下图所示:

image.png

所有的 vm_area​ 通过链表和红黑树组织,在进行内存分配时,内核会从低地址往高地址遍历 vm_area​ 搜索其中处于空闲状态的地址区间,当 空闲空间大小 大于 申请大小 时就会把这个虚拟地址区间分配出去,类似于 ptmalloc​ 中 unsorted bin 的分配机制。

image.png

申请到虚拟地址空间后会调用 alloc_page​ 一次一页地向伙伴系统申请页面,比如请求分配 10​ 页,就会调用10​次 alloc_page​。

在释放 VMALLOC​ 中的内存时,首先会把虚拟地址空间中的物理地址释放回伙伴系统,但是对应的虚拟地址空间不会立马被释放,而是会被标记为 unpurged vm_area​ ,当所有 unpurged vm_area​ 的页面数大于一个阈值(ubuntu 20.10 上是 40000 页左右)时才会对这些 unpurged vm_area​ 进行回收,在 unpurged vm_area​ 被回收前其对应的虚拟地址是不可被申请的,这个特点在后面进行内存布局的时候需要考虑。

image.png

非常直观的想法是通过越界写修改其他进程的内核栈,类似 SVE-2020-18610 的利用策略,修改 do_select 栈内存实现栈溢出和 ROP,但是 CONFIG_RANDOMIZE_KSTACK_OFFSET​ 通过给栈布局引入随机性导致偏移无法确定,从而拦截了这种利用手段

VMALLOC 上面除了内核栈外还有很多其他用于的对象(比如安卓下的 Mali GPU 驱动管理结构), ebpf 字节码等,之前 CVE-2021-33909 的利用策略就是通过 vmalloc 越界写修改 ebpf 字节码完成利用

image.png

由于前期 ebpf 漏洞过多且容易用于写恶意软件,目前低权限进程无法直接使用 eBPF,不过可以通过 setsockopt 的方式创建 ebpf 字节码(参考 CVE-2023-3609

image.png

ebpf 的处理流程如下,首先会校验字节码,校验通过后将 filter 字节码转换为 ebpf 字节码并进行 jit

image.png

通过漏洞在校验通过生成 ebpf 字节码后, jit 前篡改 ebpf 字节码就能注入恶意 epbf 指令,形成任意地址写.

image.png

篡改注入 BPF_STX_MEM 做任意地址写,修改 modprobe_path 实现提权:

image.png

其他问题:

  • 利用 CVE-2024-26816 泄露内核镜像基地址
  • 系统大量的进程创建、销毁导致内存布局不稳定:通过 SIGSTOP 暂停部分进程,让系统相关行为减少,增加稳定性
  • 通过 RACE 在 ebpf 字节码生成 — jit 中间触发漏洞:增加的 bpf 指令数目增加时间窗

​​

总结

VMALLOC 区域的溢出、越界写漏洞最近讨论的比较多:

此外通过 RACE 在 ebpf 生成过程中篡改 ebpf 指令实现任意地址写的利用策略,在其他 vmalloc 相关漏洞利用中可以使用

image.png

相关链接

  • 发表于 2024-12-13 10:16:35
  • 阅读 ( 2266 )
  • 分类:二进制

0 条评论

hac425
hac425

19 篇文章

站长统计