CVE-2022-0995 Linux kernel 观测队列子系统堆溢出漏洞分析
字数 1264 2025-08-29 08:32:18

Linux内核观测队列子系统堆溢出漏洞(CVE-2022-0995)分析与利用

漏洞概述

CVE-2022-0995是Linux内核观测队列事件通知子系统(watch_queue event notification subsystem)中的一个堆溢出漏洞。该漏洞自内核版本5.8引入,在5.17-rc7版本中被修复,CVSS评分为7.1。

基础知识

通用通知机制(General notification mechanism)

通用通知机制建立在标准管道驱动之上,可以将内核通知消息拼接到用户打开的管道中。通过CONFIG_WATCH_QUEUE编译选项启用(默认开启)。

关键特性:

  • 通过特殊模式打开的管道实现
  • 消息保存在管道内部的循环环形缓冲区(pipe_buffer队列)
  • 通过read()读取
  • 禁用splice功能以防止内容还原时与通知消息交织

Watch Queue API

主要API函数:

struct watch_queue *get_watch_queue(int fd);  // 获取观测队列引用
void put_watch_queue(struct watch_queue *wqueue);  // 丢弃引用

事件过滤器(Event Filter)

可以设置过滤器限制接收的事件:

struct watch_notification_filter filter = { ... };
ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter);

数据结构:

struct watch_notification_filter {
    __u32 nr_filters;
    __u32 __reserved;
    struct watch_notification_type_filter filters[];
};

struct watch_notification_type_filter {
    __u32 type;
    __u32 info_filter;
    __u32 info_mask;
    __u32 subtype_filter[8];
};

漏洞分析

漏洞位于watch_queue_set_filter()函数中,具体问题:

  1. 不一致的类型检查

    • 计算nr_filter时检查type < sizeof(wfilter->type_filter)*8
    • 实际拷贝时检查type < sizeof(wfilter->type_filter)*BITS_PER_LONG
    • 在64位系统上,前者检查type<0x80,后者检查type<0x400
  2. 越界写1位漏洞

    • 使用__set_bit()设置位时,可以越界修改相邻内存

漏洞利用

利用步骤:

Step I: 堆喷msg_msg构造重叠对象

  1. 创建多个消息队列,每个队列发送两条消息(主消息96字节,辅助消息0x400字节)
  2. 释放部分主消息制造空洞
  3. 触发漏洞使两个主消息指向同一个辅助消息

Step II: 构造UAF

  1. 释放辅助消息,但仍有消息队列可以访问它

Step III: 堆喷sk_buff泄露地址

  1. 使用sk_buff伪造msg_msg结构
  2. 通过越界读取泄露堆地址

Step IV: 堆喷pipe_buffer泄露内核基址

  1. 修复辅助消息头部
  2. 喷射pipe_buffer结构
  3. 通过anon_pipe_buf_ops指针计算内核基址

Step V: 劫持控制流

  1. 伪造pipe_buffer的ops指针
  2. 构造ROP链
  3. 关闭管道触发release函数指针执行ROP

漏洞修复

修复commit主要修改:

  1. 统一类型检查条件为type >= WATCH_TYPE__NR
  2. 将type范围限定为WATCH_TYPE__NR(值为2)

完整利用代码

// 完整利用代码见原文
// 主要包含以下关键部分:
// 1. 堆喷msg_msg构造重叠
// 2. 触发OOB写
// 3. 泄露地址
// 4. 构造ROP链
// 5. 提权

总结

该漏洞利用需要深入理解Linux内核多个子系统:

  1. 观测队列机制
  2. 消息队列实现
  3. 网络栈sk_buff结构
  4. 管道机制

利用技巧结合了:

  • 堆布局控制
  • 类型混淆
  • UAF
  • 内存泄露
  • ROP链构造

修复方案通过统一类型检查条件彻底解决了问题。

Linux内核观测队列子系统堆溢出漏洞(CVE-2022-0995)分析与利用 漏洞概述 CVE-2022-0995是Linux内核观测队列事件通知子系统(watch_ queue event notification subsystem)中的一个堆溢出漏洞。该漏洞自内核版本5.8引入,在5.17-rc7版本中被修复,CVSS评分为7.1。 基础知识 通用通知机制(General notification mechanism) 通用通知机制建立在标准管道驱动之上,可以将内核通知消息拼接到用户打开的管道中。通过 CONFIG_WATCH_QUEUE 编译选项启用(默认开启)。 关键特性: 通过特殊模式打开的管道实现 消息保存在管道内部的循环环形缓冲区(pipe_ buffer队列) 通过read()读取 禁用splice功能以防止内容还原时与通知消息交织 Watch Queue API 主要API函数: 事件过滤器(Event Filter) 可以设置过滤器限制接收的事件: 数据结构: 漏洞分析 漏洞位于 watch_queue_set_filter() 函数中,具体问题: 不一致的类型检查 : 计算 nr_filter 时检查 type < sizeof(wfilter->type_filter)*8 实际拷贝时检查 type < sizeof(wfilter->type_filter)*BITS_PER_LONG 在64位系统上,前者检查type<0x80,后者检查type <0x400 越界写1位漏洞 : 使用 __set_bit() 设置位时,可以越界修改相邻内存 漏洞利用 利用步骤: Step I: 堆喷msg_ msg构造重叠对象 创建多个消息队列,每个队列发送两条消息(主消息96字节,辅助消息0x400字节) 释放部分主消息制造空洞 触发漏洞使两个主消息指向同一个辅助消息 Step II: 构造UAF 释放辅助消息,但仍有消息队列可以访问它 Step III: 堆喷sk_ buff泄露地址 使用sk_ buff伪造msg_ msg结构 通过越界读取泄露堆地址 Step IV: 堆喷pipe_ buffer泄露内核基址 修复辅助消息头部 喷射pipe_ buffer结构 通过anon_ pipe_ buf_ ops指针计算内核基址 Step V: 劫持控制流 伪造pipe_ buffer的ops指针 构造ROP链 关闭管道触发release函数指针执行ROP 漏洞修复 修复commit主要修改: 统一类型检查条件为 type >= WATCH_TYPE__NR 将type范围限定为WATCH_ TYPE__ NR(值为2) 完整利用代码 总结 该漏洞利用需要深入理解Linux内核多个子系统: 观测队列机制 消息队列实现 网络栈sk_ buff结构 管道机制 利用技巧结合了: 堆布局控制 类型混淆 UAF 内存泄露 ROP链构造 修复方案通过统一类型检查条件彻底解决了问题。