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()函数中,具体问题:
-
不一致的类型检查:
- 计算
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)
完整利用代码
// 完整利用代码见原文
// 主要包含以下关键部分:
// 1. 堆喷msg_msg构造重叠
// 2. 触发OOB写
// 3. 泄露地址
// 4. 构造ROP链
// 5. 提权
总结
该漏洞利用需要深入理解Linux内核多个子系统:
- 观测队列机制
- 消息队列实现
- 网络栈sk_buff结构
- 管道机制
利用技巧结合了:
- 堆布局控制
- 类型混淆
- UAF
- 内存泄露
- ROP链构造
修复方案通过统一类型检查条件彻底解决了问题。