MS08-025 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 we are aware of.

One of these vulnerabilities deals on how we can bypass some of the ProbeForWrite and ProbeForRead checks when using user supplied memory pointers.

ProbeForWrite/Read are functions that will check if a provided address range (base + length) has the following properties (throwing an exception in case it fails):

·         The range is inside the user mode virtual memory space.

·         ProbeForWrite will check if the range is writable.

·         By design, if the length of the range equals to zero, ProbeForRead and ProbeForWrite will not throw an exception and the normal flow of this request will be taken (the range will be validated).

Find out more information about these functions at: http://msdn2.microsoft.com/en-us/library/ms809962.aspx and http://msdn2.microsoft.com/en-us/library/ms790786.aspx

In the case of this bulletin, using a specially crafted request to win32k from user mode we can overflow the length variable, which can then take the erroneous value of zero, leading to the probing function being bypassed.

One of the approaches that we could have taken was to fix these functions and throw an exception when we receive a zero length. This was not a good approach since it will break lots of interfaces. On win32k there are some functions that upon receiving a zero length they will return with the needed length for a correct request. Updating the probe functions to blindly reject zero length values would break these usages.

Instead we decided to check each of the probes looking for integer overflows that will lead to length zero. These are the cases we and drivers developers need to take care of:

// lParam and wParam are untrusted DWORDs since they come from user mode

try {
        str.bAnsi = bAnsi;
        str.MaximumLength = (ULONG)wParam;
        if (!bAnsi) {
            str.MaximumLength *= sizeof(WCHAR);   // we can overflow this max length and lead to zero
        }

        str.Length = 0;
        str.Buffer = (LPBYTE)lParam;

        ProbeForWrite((PVOID)str.Buffer, str.MaximumLength, sizeof(BYTE));
    
    } except (StubExceptionHandler(FALSE)) {
          MSGERROR(0);
    }

 [... later write into str.Buffer pointer based on wParam ...]

As you can see we bypass the ProbeForWrite check with the overflow (wParam = 0x80000000 that leads to str.MaximumLength = 0). Once this check has been passed, it depends on the function we are targeting but a ProbeForWrite seems like later we are going to write to the supplied pointer (lPram).

And these are the cases where we can have a legitimate use of zero length on the probe:

// lParam and wParam are untrusted DWORDs since they come from user mode

try {
        str.bAnsi = bAnsi;
        str.MaximumLength = (ULONG)wParam;

        str.Length = 0;
        str.Buffer = (LPBYTE)lParam;

        ProbeForWrite((PVOID)str.Buffer, str.MaximumLength, sizeof(BYTE));
    
    } except (StubExceptionHandler(FALSE)) {
          MSGERROR(0);
    }

    if (str.MaximumLength==0) {
               retval = STATUS_BUFFER_TOO_SMALL;
              *size_needed= sizeof(struct_to_copy_from);
              return retval:
    }

[... later checks for the size needed may apply ...]

In this case it is by design on some API that if you pass a zero length the call will fail and you will get back the needed size for this particular API call.

- Security Vulnerability Research & Defense Bloggers

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