A simple protection against HMValidateHandle technique

In the recent days I was reading technical analysis of win32k exploits from recent years, and it caught my eyes, that the HMValidateHandle technique is very heavily used almost everywhere. Then I had an idea how to protect against this family of exploits, which I think is very simple. This post will be about that.

What is HMValidateHandle?

HMValidateHandle is an internal, unexported function of user32.dll. It takes a handle and a handle type as arguments, and by looking up the handle table, if the handle is matching with the type it will copy the object to user memory. If the object contains a pointer to itself, like tagWND it can be used to leak memory addresses from the kernel. This has been a known technique for very long time, I think the first mention of this was in Tarjei Mandt’s 2011 BlackHat US talk, you can find the PDF here: https://media.blackhat.com/bh-us-11/Mandt/BH_US_11_Mandt_win32k_WP.pdf There are awful lot of documentation about this, and it was widely abused in many-many Windows kernel exploits, as you could reliably leak kernel object addresses, especially useful for kernel pool spraying. Thus Microsoft decided to finally close this, and so this technique doesn’t work beyond Windows 10 RS4.

Getting the address of HMValidateHandle

Conceptually it’s very simple. This function is frequently called from inside user32.dll so we just need to parse through it, and grab / calculate its address. The most common technique is locating the IsMenu function in user32.dll:

HMODULE hUser32 = LoadLibraryA("user32.dll");
BYTE* pIsMenu = (BYTE *)GetProcAddress(hUser32, "IsMenu");

Then parsing through it, and calculate the address of it. It’s being called very early in the function, and the opcode E8 will indicate that, we can go from there.

unsigned int uiHMValidateHandleOffset = 0;
	for (unsigned int i = 0; i < 0x1000; i++) {
		BYTE* test = pIsMenu + i;
		if (*test == 0xE8) {
			uiHMValidateHandleOffset = i + 1;
			break;
		}
	}
	unsigned int addr = *(unsigned int *)(pIsMenu + uiHMValidateHandleOffset);
	unsigned int offset = ((unsigned int)pIsMenu - (unsigned int)hUser32) + addr;
	//The +11 is to skip the padding bytes as on Windows 10 these aren't nops
	pHmValidateHandle = (lHMValidateHandle)((ULONG_PTR)hUser32 + offset + 11);

The most well known implementation (from where the above code is also taken) can be found here: windows_kernel_address_leaks/HMValidateHandle at master · sam-b/windows_kernel_address_leaks · GitHub

Here is how IsMenu function looks like in the latest Windows 10 version: image

Simple enough? Likely this is why this was chosen. However you could chose many other functions, as you can see below. image

For example picking up GetMenuItemCount would be similarly easy: image

Likely the only thing you would need to change is this:

BYTE* pIsMenu = (BYTE *)GetProcAddress(hUser32, "IsMenu");

to:

BYTE* pGetMenuItemCount = (BYTE *)GetProcAddress(hUser32, "GetMenuItemCount");

and maybe the offset calculation at the end because of some nops, but otherwise easy-peasy.

In older versions of Windows it was a bit less used, but still pretty good, this one is from Windows 7 x86: image

Protecting against the technique

Although with Windows 10 RS4 the problem is gone, there are still a big amount of machines out there, which are affected. The idea is very simple. Under normal circumstances HMValidateHandle will be only called from inside user32.dll. If we hook this function in user mode, and then check the call stack, we can easily spot if the call is a valid call, or an exploit abusing it with directly calling it from outside user32.dll. Most AV vendors implement user mode hooks as you can’t hook everything in kernel mode (like this), so adding one more, really shouldn’t be that difficult and it would significantly increase protection against such exploits.

I personally never implemented hooking in Windows, so wanted to experiment with this and see if my idea works or not. I saw earlier that some AV products use Microsoft’s Detours, so I thought that will be good for me as well.

Detours installation

You can get the Microsoft Detours source code from here: Releases · microsoft/Detours · GitHub. Once downloaded, open a VS Developer Command Prompt, and type: nmake This will build all the examples, create the lib files, etc… Now repeat the same with the x86 / x64 version of the Developer Command Prompt, in case you need both versions. Place the include and lib files to the location of your choice.

Integrate with Visual Studio

In you C / C++ code you need to include the header:

#include <detours.h>

In the Project -> C/C++ -> General -> Additional Include Directories add the folder where the header files present. image

And under Project -> Linker -> General -> Additional Library Directories add the folder where the compiled lib files present. image

That’s all you need.

Using Detours

It is super easy. I’m not a coder at all, but it was easy even for me. Essentially you can take this example from the official WiKi page: Using Detours · microsoft/Detours Wiki · GitHub and change it. This is what I did. This is how you hook:

DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)pHmValidateHandle, ProtectHMValidateHandle);
DetourTransactionCommit();

and this is how you unhook:

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)pHmValidateHandle, ProtectHMValidateHandle);
DetourTransactionCommit();

pHmValidateHandle contains the address of the original function you want to hook and ProtectHMValidateHandle is my function which is implementing the alternate functionality.

Implementing the protection

Again the basic idea is to verify if the call is coming from user32.dll vs. somewhere else. For this we need to verify the call stack. First I was afraid that I will need to do some assembly magic for that, but luckily Windows has an API to retrieve items from the stack, and the function is: CaptureStackBackTrace, MSDN: CaptureStackBackTrace function - Windows applications | Microsoft Docs. It looks like this:

USHORT WINAPI CaptureStackBackTrace(
  _In_      ULONG  FramesToSkip,
  _In_      ULONG  FramesToCapture,
  _Out_     PVOID  *BackTrace,
  _Out_opt_ PULONG BackTraceHash
);

What is important to us is: FramesToSkip will tell the function how many frames to skip when getting values. I set it to 0, so we don’t skip any (although could set 1, see later why). FramesToCapture will define how many entries we want. BackTrace is a pointer to a location, which can hold the data read from the stack.

I read two frames from the stack (or could skip the first and read only the 2nd), because: 1st frame - will be the trampoline function that is present because of the hook. 2nd frame - the original caller of the HMValidateHandle function.

To demonstrate this here is an output from my hook:

[*] Code section: `.text'
[*] Base address : 0x7ffe765d0000
[*] Start address: 0x7ffe765d1000
[*] End address  : 0x7ffe7665708d

[i] IsMenu being called
[*] Hooked function called
[  0] = 00007FFE699210CC
[  1] = 00007FFE765FBE6B
[i] IsMenu called

[*] Hooked function called
[  0] = 00007FFE699210CC
[  1] = 00007FF73C601278

We can see that that the 1st frame is always pointing to the same place, the trampoline function. The 2nd frame is the original caller, in case of IsMenu the address falls in the range of the user32.dll code segment, while in the second case where I call the function directly, like you would do in an exploit, the return address is pointing to somewhere else.

Essentially the entire protector function looks like this:

#ifdef _WIN64
VOID* WINAPI ProtectHMValidateHandle(HWND h, int type)
#else
PVOID __fastcall ProtectHMValidateHandle(HWND h, int type)
#endif
{
	printf("[*] Hooked function called\n");
	void* stack[STACK_ENTRIES] = { 0 }; 
	WORD numberOfFrames = CaptureStackBackTrace(0, STACK_ENTRIES, stack, NULL);
	for (USHORT iFrame = 0; iFrame < numberOfFrames; ++iFrame) {
		printf("[%3d] = %p\n", iFrame, stack[iFrame]);
	}
	
	//the 1st item on the stack is the hook, the 2nd item is the original caller, which should be always originate from user32.dll as the function is not exported
	//verify if the call origin is inside the user32.dll code segment
	if ((SIZE_T)stack[1] < user32_dll_start || (SIZE_T)stack[1] > user32_dll_end)
	{
		printf("[\\o/] Exploit detected, exiting program...\n");
		ExitProcess(-1);
	}
	//call the original function
	return pHmValidateHandle(h, type);
}

If I see an exploit attempt, I simply quit the process. I put the entire code to GitHub under my kex library: kex/HookHMVH at master · theevilbit/kex · GitHub

I also put there a tester code, which is mainly this, with a few changes: windows_kernel_address_leaks/HMValidateHandle.cpp at master · sam-b/windows_kernel_address_leaks · GitHub

Testing

To test it with a real world exploit, not just a test app I picked up a recent exploit for CVE-2019-1132 which can be downloaded from here: GitHub - Vlad-tri/CVE-2019-1132: EoP POC for CVE-2019-1132 To simulate the hook of an AV vendor I put a line to the beginning of the exploit, to load my DLL:

LoadLibraryA("HookHMVH.dll");

and compiled it again with that. The normal version worked as expected: image

However when I run it with my DLL loaded, it failed: image

(…)

image

Hook every process

It was outside of my attempt to universal hook every process in the system, you will likely need a kernel driver for that. AppInit_DLLs won’t work here, because it’s being loaded when DLL_PROCESS_ATTACH is hit in user32.dll, so that DLL is not loaded yet, in fact you can rely only on ntdll.dll and kernel32.dll at that stage.

Conclusion of the protection

Although I didn’t test it extensively if it creates false positives or not - although if yes I would take a look at that binary - I’m pretty confident that it should work universally. It will handle all cases, no matter from where you get HMValidateHandle’s address. Not sure if any exploit protection products are using this trick, but it should be pretty easy.

Note - UPDATE 2019.08.01.

As noted by others, I almost forget to mention one thing. Obviously you can bypass this protection if you mimic what the function does, and never call it. Essentially there is another well known technique for achieving the same, which is getting to the DesktopHeap where all these structures are stored and parse the heap. This method is beyond the scope of this post, but if you want to read about it, here are some awesome resources:

https://www.blackhat.com/docs/us-17/wednesday/us-17-Schenk-Taking-Windows-10-Kernel-Exploitation-To-The-Next-Level%E2%80%93Leveraging-Write-What-Where-Vulnerabilities-In-Creators-Update.pdf

FuzzySecurity | Windows ExploitDev: Part 18

https://www.offensive-security.com/vulndev/development-of-a-new-windows-10-kaslr-bypass-in-one-windbg-command/

https://twitter.com/blomster81/status/844544024224710656

windows_kernel_address_leaks/DesktopHeap at master · sam-b/windows_kernel_address_leaks · GitHub