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? Link to heading
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 Link to heading
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:
Simple enough? Likely this is why this was chosen. However you could chose many other functions, as you can see below.
For example picking up GetMenuItemCount
would be similarly easy:
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:
Protecting against the technique Link to heading
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 Link to heading
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 Link to heading
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.
And under Project -> Linker -> General -> Additional Library Directories
add the folder where the compiled lib files present.
That’s all you need.
Using Detours Link to heading
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 Link to heading
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 Link to heading
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:
However when I run it with my DLL loaded, it failed:
(…)
Hook every process Link to heading
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 Link to heading
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. Link to heading
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:
FuzzySecurity | Windows ExploitDev: Part 18
https://twitter.com/blomster81/status/844544024224710656
windows_kernel_address_leaks/DesktopHeap at master · sam-b/windows_kernel_address_leaks · GitHub