In my last post we looked at how to hook the local PowerShell process and adjust the date by hooking the GetSystemTimeAsFileTime WinAPI function. This was accomplished using EasyHook, a detouring library similar to Microsoft Detours. In this post I’ll show you how to hook a remote process and inject our own implementation of a function. EasyHook offers a well defined interface for accomplishing this. I took some time and wrapped this is an easily-consumed PowerShell function.
EasyHook’s remote hooking functionality is accomplished by injecting a DLL in the target process, creating a thread remotely and executing the entry point for that DLL. EasyHook then creates a CLR host and loads up the managed hook. This allows managed code to hook unmanaged processes. EasyHook can communicate between the hooked process and hooking process via IPC. This allows for parameters and even objects to be communicated to the hook.
In my last post, we hooked something that isn’t too interesting. Changing the time is a neat trick but not all the helpful. This time, I’ve picked a target that is a bit more malicious. I hope to demonstrate that it’s a good idea to verify where your scripts are coming from before launching them. In this post we will look at hooking CryptProtectData. This function is part of the Data Protection API built into Windows. This function is used by numerous Windows products including the Credential Manager to store user names and passwords remembered by the system.
The downfall of any cryptography is that the plaintext must be transferred into encryption method to be processed. Since CryptProtectData is a well-known entry point, it is a candidate to compromise.
I’ve made a very simple console application encrypts a password using CryptProtectData. The console conviently waits for user input so that we can inject the hook before continuing.
The function’s signature is very similar to the New-Detour function we went through last time. Parameters were slightly changed to accept a process and some additional C# code that is compiled into the hook.
The AST is still used to parse out the parameters from the script block to help generate the signature for the delegate and method used for the hook. EasyHook’s remote hooking process relies on the fact that a class implements the IEntryPoint interface and Run method. Also, the proper constructor must be present in order to enable EasyHook to instantiate the hook. The Run method actually performs the local hooking that we did last time around. Once the Run method returns, the hook will be removed. The hook demonstrated below blocks forever using a ManualResetEvent.
Once, compiled we will need to ensure that the hook is accessible by EasyHook. In my case, I copied EasyHook and the auto-generated DLL into the same directory as the process. Then we can call the RemoteHooking.Inject method. It’s also possible to register EasyHook and the hook DLL into the GAC. EasyHook will actually take care of removing it once the hooking has finished.
Once the function is defined we can use it pretty easily to inject into a remote process. In the case of CryptProtectData, we need to define a struct that will be passed into the hooked function. The signature will be included with the compilation of the hooked DLL. Once inside the hook we can get the pointer to the data stored within the struct and convert it back to a managed string using the Marshal.PtrToStringUni method. Then the password is output to my desktop in a text file.
Isn’t that a little scary? And I thought I was being safe by using encryption.
Although this is pretty crazy there are some things to note about this entire process.
- You’ll likely need to be an administrator to copy files or register them in the GAC
- Although it’s possible to make the hook more robust, this hook would fail if anything except a Unicode string was passed into the function.
- I had to crash the target process a couple dozen times before I got this right. Injection made the process very unstable.
- Many times, hooks will perform as a pass through and call the real function. If this was the case it might not be entirely obvious that a hook is recording your password.
Some caveats and tips if you want to try this your self.
EasyHook was loading the wrong version of the .NET framework when I was loading it into processes. It would load the v2 version and I was compiling for v4. I had to modify the EasyHook code base to get it load the correct version. The CorBindToRuntime function was being called with a NULL version which typically will load the newest version, but this wasn’t the case for some reason. I just hard coded v4 in the EntryPoint.cpp file.
Remote hooking can fall down in so many ways. EasyHook will log to the event log if anything bad should happen. If you have UAC on, you’ll have to elevate the process to get this events logged. I found them extremely helpful. Additionally, don’t forget about Fusion logging as it will tell you a lot about what is being loaded.
Now I bet you’ll double check a script before executing it on your machine.