Skip to main content
MSRC

MS08-061 : The case of the kernel mode double-fetch

MS08-061 addresses several vulnerabilities in win32k.sys where you can execute arbitrary code in kernel mode. These bugs can only be exploited locally and there is no remote vector based on our investigation of the vulnerability.

One of these vulnerabilities involves multiple kernel mode accesses of user mode data leading to an interesting race condition. When accessing user mode data, kernel mode code needs to “capture” a copy of the user mode data locally and avoid accessing the user mode original multiple times. Failing to do so results in a type of problem known as a “double fetch”.

When the kernel captures the user mode data, it works with the local copy (located in kernel address space) and is not affected by any changes that happen to the user mode copy. This capture avoids race conditions involving different values of the data being returned from subsequent user mode fetches.

A double fetch from user mode could lead to several security vulnerabilities. In the case of MS08-061, we are addressing an inadequate pool allocation and a later memory pool overflow. Let’s look at a piece of the vulnerable source code:

// Attacker controls lParam
void win32k_entry_point(...) {
   […]
      // lParam has already passed successfully the ProbeForRead

      my_struct = (PMY_STRUCT)lParam;
      if (my_struct ->lpData) {
           cbCapture = sizeof(MY_STRUCT) + my_struct->cbData;  // [1] first fetch
    […]
                 // my_struct ->lpData has already passed successfully the ProbeForRead
    […]
            if ( my_allocation = UserAllocPoolWithQuota(cbCapture, TAG_SMS_CAPTURE)) != NULL) {
                 RtlCopyMemory(my_allocation, my_struct->lpData, my_struct->cbData);   // [2] second fetch
            }
      }
   […]
}

As you can see in the above vulnerable code, there are two fetches of the same user mode data ([1] and [2]). Since the kernel cannot guarantee that both fetches will have the same value, a small allocation can occur in [1] but later in [2] the kernel copies the data with a bigger length causing a memory pool overflow. This can easily be performed with a parallel, and high priority, thread changing the supplied address range (pointed by lParam) in a loop.

A kernel developer should keep these issues in mind and should capture whatever data the code is using from user mode avoiding double fetches.

// Attacker controls lParam
void win32k_entry_point(...) {
  […]
     // lParam has already passed successfully the 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 has already passed successfully the ProbeForRead
    […]

         if ( my_allocation = UserAllocPoolWithQuota(cbCapture, TAG_SMS_CAPTURE)) != NULL) {
             RtlCopyMemory(my_allocation, lpData_captured, cbData_captured);
         }
      }
    […]
}

- Fermin J. Serna, SVRD Blogger

*Postings are provided “AS IS” with no warranties, and confers no rights.*


Related Posts

How satisfied are you with the MSRC Blog?

Rating

Feedback * (required)

Your detailed feedback helps us improve your experience. Please enter between 10 and 2,000 characters.

Thank you for your feedback!

We'll review your input and work on improving the site.