User-Mode Hooking

Why build more systems when most EDRs have hooking in place?

If hooking from the kernel has proved itself, why not implement it in the user-mode? We think that even though a method like this is a little more easy to bypass, every additional mitigation may potentially reduce the probability to a successful exploitation. As we saw earlier, the main target of our hooking was to capture data passed to the function DeviceIoControl, which allows us to send I/O requests to a specific driver and use its built-in IOCTL code to perform some action. So as another defense mechanism against the attacks we presented to you, we’ll hook the use of DeviceIoControl in the user mode by injecting DLL which monitors for the use of the function in the user mode application, and block it if it tries to make read/write operations to a known malicious IOCTL code in a known vulnerable name.

The Injector

For PoC purposes, we’re searching for an application called “POC.exe” and injecting the DLL we created using an open-source library called “EasyHook”. We used this and did not write the hook ourselves, since most endpoint products already hold their own hooking framework, as such the importance of this section is the hook logic.

The iteration was done with the CreateToolhelp32Snapshot function. Next, validate that the process is still alive with GetExitCodeProcess:

If the process is still alive, we’re using the function RhInjectLibrary from the “EasyHook” library, the parameters passed to the function “EASYHOOK_INJECT_DEFUALT” is used to set the injection type to default, which means that a library of our choice will be injected into the remote process (as you can see in the code the parameter specify the library is dllToInject. Upon successful injection, the DLL is loaded into the process and we jump right to installing the hook.

Installing the Hook on DeviceIoControl

Once the DLL has been attached to the process, the following function will initiate the installation of the hook on DeviceIoControl:

EasyHook has come to our help once again, by using the function LhInstallHook provided by the EasyHook library, we install the hook on DeviceIoControl located in kernel32.dll, which is not a native function, but good enough to capture I/O requests sent to the vulnerable driver. The native for this call is NtDeviceIoControlFile The function LhSetExclusiveACL is used to set the hook on all threads of the process. Once the installation part has been done, all threads in the process that call the function DeviceIoControl will be jump to the hook function MyDeviceIoControlHook before proceeding.

MyDeviceIoControlHook – The Gate Before the I/O Request

Now that we hooked DeviceIoControl, We first need to analyze the parameters and make sure that first, the application does not send the I/O request to a vulnerable driver, secondly, the application does not use any IOCTL for writing or reading from the kernel memory using the vulnerable driver.

We’ve shrunk it into one if:

The function GetNtPathFromHandle is a custom-made function to get the name of the target driver, once the name has been acquired, we’re checking if the name contains the string DBUtil_2_3, and if the IOCTL code sent to the driver is for reading/writing from/to the kernel memory, if it does, we’ll block the request from reaching the kernel space and stop the procedure in the user-mode.

Notice that the PoC has focused only on the DBUtil_2_3 driver but can be applied to every driver you desire to block. All that one needs to do in order to make this functionality into a detection against BYOVD is to check for a list of vulnerable drivers symbolic links, in conjunction with their relevant IOCTL to block.

Last updated