内核模式双重获取漏洞案例:MS08-061深度解析

本文深入分析MS08-061安全公告中的内核模式双重获取漏洞,探讨win32k.sys中因多次访问用户模式数据导致的竞态条件问题,包括漏洞代码示例和修复方案,帮助开发者避免类似安全风险。

MS08-061:内核模式双重获取案例

MS08-061解决了win32k.sys中的多个漏洞,攻击者可在内核模式下执行任意代码。这些漏洞仅能本地利用,根据我们的调查,不存在远程攻击向量。

其中一个漏洞涉及多次内核模式访问用户模式数据,导致有趣的竞态条件问题。当访问用户模式数据时,内核模式代码需要“捕获”用户模式数据的本地副本,避免多次访问原始用户模式数据。未能这样做会导致称为“双重获取”的问题类型。

当内核捕获用户模式数据时,它处理本地副本(位于内核地址空间),不受用户模式副本任何更改的影响。这种捕获避免了因后续用户模式获取返回不同数据值而引发的竞态条件。

从用户模式双重获取可能导致多种安全漏洞。在MS08-061案例中,我们解决了不充分的池分配和后续的内存池溢出问题。让我们查看一段易受攻击的源代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 攻击者控制lParam
void win32k_entry_point(...) {
   []
      // lParam已成功通过ProbeForRead检查

      my_struct = (PMY_STRUCT)lParam;
      if (my_struct ->lpData) {
           cbCapture = sizeof(MY_STRUCT) + my_struct->cbData;  // [1] 第一次获取
    []
                 // my_struct->lpData已成功通过ProbeForRead检查
    []
            if ( my_allocation = UserAllocPoolWithQuota(cbCapture, TAG_SMS_CAPTURE)) != NULL) {
                 RtlCopyMemory(my_allocation, my_struct->lpData, my_struct->cbData);   // [2] 第二次获取
            }
      }
   []
}

如上所示易受攻击的代码中,存在对同一用户模式数据的两次获取([1]和[2])。由于内核无法保证两次获取的值相同,可能在[1]处发生小分配,但在[2]处内核使用更大长度复制数据,导致内存池溢出。这可以通过并行高优先级线程循环更改提供的地址范围(由lParam指向)轻松实现。

内核开发者应牢记这些问题,应捕获代码从用户模式使用的任何数据,避免双重获取。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// 攻击者控制lParam
void win32k_entry_point(...) {
  []
     // lParam已成功通过ProbeForRead检查

     my_struct = (PMY_STRUCT)lParam;
     cbData_captured= my_struct->cbData;
     lpData_captured = my_struct->lpData;
     if (lpData_captured) {
         cbCapture = sizeof(MY_STRUCT) + cbData_captured;

    []
         // lpData_captured已成功通过ProbeForRead检查
    []

         if ( my_allocation = UserAllocPoolWithQuota(cbCapture, TAG_SMS_CAPTURE)) != NULL) {
             RtlCopyMemory(my_allocation, lpData_captured, cbData_captured);
         }
      }
    []
}
  • Fermin J. Serna, SVRD Blogger 文章按“原样”提供,不提供任何担保,也不授予任何权利。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计