Using API Hooking to Dump LSASS with Task Manager Undetected

by | Apr 10, 2023 | Blog

There are many ways to create an LSASS dump file. One of the easiest ways is with Windows Task Manager. Simply right click the LSASS process and click “Create dump file”. This is great, except for the fact that Windows Defender will immediately flag this as malicious. Far from stealthy. Not ideal.

This raised some interesting questions. What is it about Task Manager that triggers detection so quickly? One of the main differences is when dumping a process through Task Manager is that you cannot change the name of the output file. It will just be the name of the process. In our case, “lsass.DMP”. So could it be that Defender sees that file getting created and that triggers the alert? What if we could hook the API call(s) responsible for creating the file and change it to something else more benign. It is a trusted Windows tool after all and does not allow users to change the name of the resulting dump file. Can we abuse this trust to create a LSASS dump file with a different name that will leave Defender none the wiser? Short answer, yes! This blog will serve as an introduction to Windows API hooking. First, we will monitor the API call’s made while dumping LSASS from Task Manager. Then we will create a DLL that we can inject into the Task Manager process that will hook these API calls responsible for file creation and change the name of the LSASS dump file that gets created.

Short Introduction to API Hooking: In order to change the name of the resulting dump file, we need to identify and “hook” these API calls that are responsible for creating the file so that we can modify the filename to our liking. To do this, we will use the Detours library. Installation and setup of this library is outside the scope of this blog and is left as an exercise for the reader.

According to the Detours Wiki: “Detours replaces the first few instructions of the target function with an unconditional jump to the user-provided detour function. Instructions from the target function are placed in a trampoline. The address of the trampoline is placed in a target pointer. “

Essentially, once the target function is called, we will “jump” to our detour function. This is the function that we control. We can read the parameters to the function, modify them, or perform other actions before passing execution back to the original function. In our case, our detour function will modify the file path from the default: C:\Users\<username>\AppData\Local\Temp\lsass.DMP to something else.

https://www.cs.columbia.edu/~junfeng/10fa-e6998/papers/detours.pdf

To identify the API calls we need to hook, we will use the tool API Monitor to, as the name suggests, monitor the API calls that are made when the dump file is created. We can then search through the output for a portion of our string “AppData\Local\Temp\lsass.DMP” and find the functions where it is being used.

Because dumping LSASS requires administrative privileges, we will start Task Manager and API Monitor as Administrator.

Before monitoring the process, we will select some API filters to tell API Monitor what to log. Since an exorbitant number of API calls are made, selecting everything is not ideal. We will select a few choices that seem reasonable for what we are trying to find. I have selected “Data Access and Storage, Diagnostics, NT Native, and System Services”.

We select taskmgr.exe and begin monitoring. We create the LSASS dump file which unsurprisingly triggers Defender and kills the taskmgr process. In this short span of activity, we logged 193,416 API calls. Hopefully a few of them have what we need.

Now we need to figure out where this file is getting created. Starting on the first thread, we search for our target string and see the first call which is to RtlInitUnicodeString.

Looking at the function signature on MSDN, we can see that the second parameter is the source string, which is the path to the lsass.DMP file, and the first parameter is a pointer to a UNICODE_STRING type that will be filled by this function call.

Now looking ahead a little bit, we are trying to see where the file is getting created. Looking beyond the call to RtlInitUnicodeString, we see a call to NtCreateFile. We can inspect the parameters in API Monitor and we do see our target string deep in fields of the OBJECT_ATTRIBUTES struct.

Looking up NtCreateFile on MSDN shows that the OBJECT_ATTRIBUTES struct contains a field, ObjectName of type PUNICODE_STRING. Which according to the documentation “Points to a buffered Unicode string that names the file to be created or opened.”

Now we don’t need to hook the call to NCreateFile necessarily, we just need to find where this string is getting created and change it upstream from the call to this function.

We already know about RtlInitUnicodeString, and just below that call, there is a call to RtlDosPathNameToRelativeNtPathName_U which also contains the lsass.DMP path string we are looking for. This is an undocumented function in ntdll.dll, but we can still find it’s function signature on a site such as ReactOS, and I also found that ChatGPT is quite handy for this as well.

It mentions that NtName is a pointer to a UNICODE_STRING struct that will receive the translated NT path. This is evident as well in API Monitor and we can see this param has the same memory address as the PUNICODE_STRING passed to RtlInitUnicodeString.

Repeating this process, we identify two other API calls that are like the previous two.

RtlInitUnicodeStringEx

RtlDosPathNameToRelativeNtPathName_U_WithStatus

Finally, the last call is to SetDlgItemTextW which sets the text in the dialog box when the dump is complete. So to recap, we will need to hook the following API calls:

RtlInitUnicodeString

RtlDosPathNameToRelativeNtPathName_U

RtlInitUnicodeStringEx

RtlDosPathNameToRelativeNtPathName_U_WithStatus

SetDlgItemTextW

 

Creating our functions:

We will open Visual Studio and create a new project and select Dynamic-Link Library (DLL). Now for each of the functions we need to hook, we have to do two things. The first is to create a pointer to the function we want to hook. The second is to create the detour function itself that will be invoked when our target function is called.

We define the function pointers like so:

Notice that the first four functions we are initializing to NULL, as opposed to the last function where we are setting it to the name of the real function we will be calling. This is because the first four functions are in NTDLL.dll and even though they are exported by the library, they cannot be called directly. So, we will have to dynamically look up these functions to get their addresses. We can do this quite easily with Detours and the DetourFindFunction method.

We will also define two global variables of the path we are searching for, and what we want to replace it with.

Now we need to create the functions we want to call when our target functions are hooked. These methods MUST have the exact same signature and calling convention of the real function. Using the same calling convention ensures that registers will be properly preserved and that the stack will be properly aligned between our detour and target functions.

Since these API’s get called many times, we need to check the parameter containing the file path and see if it is the path with our lsass.DMP file. If it is, we will replace it with our new path to “normalfile.txt” and call our real target function pointer with the new variable. If it’s not, we will just call the real target function pointer with the parameters unchanged.

Now, on the call to DLL_PROCESS_ATTACH we will create a method setHooks which will contain our Detours code.

We are dynamically resolving the addresses to the functions located in NTDLL as mentioned earlier, and calling DetourAttach with a pointer to the address of our target function, and our detour function.

Similarly, on DLL_PROCESS_DETACH we are calling a removeHooks method that restores all the original code.

Now we can compile our project and inject the DLL!

For testing simplicity, I am using Process Hacker to inject our DLL into Task Manager. I also have a project, DLLInject that would be more suitable in a real-world engagement. After injecting the DLL, we can see that it has been loaded into the process.

Now, dumping the LSASS process, we can see that our file has been created without a peep from Defender!

The full source code can be found at:

https://github.com/djackreuter/taskmgr_hooking