Proxying COM For Stable Hijacks

Introduction

COM hijacking presents a unique way to load your rogue DLL into another process. In a world with increasing optics into in-memory tactics, COM hijacking may provide an alternative to get code execution inside a process. This post will attempt to explain what’s going on when a COM object is hijacked and share a method to proxy the original COM server to avoid crashing the process.

Previous Work

COM hijacking was first discussed back in 2011 by Jon Larimer who foresaw the potential security concerns of per-user COM objects. Since then there was some malware that leveraged the technique, @enigma0x3 released a solid blog post of leveraging COM hijacking and scheduled tasks for persistence in 2016, and @bohops released two posts adding more information around abusing COM. It’s also listed on MITRE ATT&CK as T1122.

When talking COM, it would also be remiss of me to not mention oleviewdotnet by James Forshaw . He also presented my favorite talk on the subject, COM in 60 Seconds, and has tons of other research surrounding COM.

tl;dr

Proxying the legitimate COM server DLL can lead to more stability and more options for COM hijacks. The PoC code can be found here: https://github.com/leoloobeek/COMProxy

Refresher

COM is very complex, I’m going to only cover the pieces that are relevant here and might “hand wave” over some details. I will refer you to the previous work above or this book if you want to go further.

Most COM classes are registered and discovered by their CLSID (class identifier) which is a GUID. If you have ever created a COM object you’ve likely given a name such as “WScript.Shell”, this is called a ProgID. When you attempt to access a COM object the process will query HKEY_CLASSES_ROOT\CLSID which contains hundreds of subkeys named for each CLSID registered on the host. Within the CLSID’s path will contain additional registry keys that the COM subsystem can use to load the correct COM server.

COM servers are essentially DLLs that provide COM interfaces to the calling process (client).  COM servers can be in-process or out-of-process, for this blog post we’ll only worry about COM in-process servers.

Hijacking COM

The issue that leads to COM hijacking is when querying HKEY_CLASSES_ROOT\CLSID, Windows will look at HKEY_CURRENT_USER\Software\Classes\CLSID for the provided CLSID. If the key does NOT exist then Windows looks in HKEY_LOCAL_MACHINE\Software\Classes\CLSID for the provided CLSID. In almost all cases COM classes are registered in HKLM. Since Windows will check HKCU first, it is possible to add a CLSID of an existing, legitimate COM class pointing to a rogue COM server, and the process will load the rogue DLL rather than the expected COM server. No adminstrator privileges required.

Lots of previous work covers how to find them, in short load up Process Monitor, filter Path looking for InProcServer32, filter for the process you want to load into, and let it run.

For example, on Windows 7, {7D096C5F-AC08-4F1F-BEB7-5C22C517CE39} is a GUID Internet Explorer queries for soon after starting. Writing out a DLL to initiate HTTPS remote access within Internet Explorer can help blend in within a scrutinized environment. Setting the registry keys as follows will set up the hijack.

Important Note: I, mistakenly, chose a Windows 7 hijack I had written down for this example. COM hijacking is absolutely still prevalent in Windows 10 environments, but with  Code Integrity Guard (CIG) it may limit which processes you will find success. I’ll leave this up to the reader as the focus is not on finding hijacks and more on the internals of COM hijacking. The PoC COM client and server for hijacking released work on both Windows 7 and 10.

Screen Shot 2019-08-28 at 7.03.36 PM.png

Opening up Internet Explorer and… we crashed the process.

Screen Shot 2019-08-28 at 7.10.50 PM.png

Well, taking a step back, we are forcing a process to load our DLL instead of the DLL it expects, and our DLL doesn’t have any of the functionality the process requires. It seems logical that the process would crash once it realizes our DLL isn’t what they’re expecting. If you’ve ever looked for COM hijacks, this can become pretty common, or worse, everything appears OK but certain features may become unstable.

What’s in a COM Server

In order to provide COM interfaces to clients, a COM server needs DllGetClassObject in its export table. Once an in-process COM server is loaded, the process will call the DLL’s DllGetClassObject with the CLSID and the server will return pointers to the interface.

Capture

When the COM object is no longer used, it should be released to free up memory. Furthermore, periodically the process should call CoFreeUnusedLibraries which will tell the process to check each loaded COM server for any COM objects instantiated. If no COM objects are instantiated, the process will call the COM server’s exported function DllCanUnloadNow. Legitimate COM servers will perform any cleanup, return to the process, and the process will free the library (FreeLibrary) from its memory.

Crafting a COM Server For Hijacking

To begin, COM servers should export DllGetClassObject and DllCanUnloadNow at a minimum. What’s interesting about DllGetClassObject is the main output from the function is a pointer to the interface. Since are an in-process COM server will be loaded into a process, and we’re returning a pointer, it’s trivial to proxy the legitimate COM object. Loading the legitimate COM server, finding its DllGetClassObject export, then calling it will obtain all the valid pointers.

typedef HRESULT(__stdcall *_DllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    // Load up the original COM server
    HMODULE hDLL = LoadLibrary("C:\\Windows\\legitimate\\COMServer.dll);
    // Find the DllGetClassObject for original COM server
    _DllGetClassObject lpGetClassObject = (_DllGetClassObject)GetProcAddress(hDLL, "DllGetClassObject");
    return lpGetClassObject(rclsid, riid, ppv);
}

At this point, the process is receiving the COM interface its expecting. Although what if the process uses the class shorter than the malicious activities you hope to accomplish? The process will release the object and call DllCanUnloadNow which will remove the rogue DLL from its memory. One (somewhat hacky) way to ensure your code runs before unloading is to use a global variable and loop when DllCanUnloadNow is entered.

UINT g_uThreadFinished;
extern UINT g_uThreadFinished;
// Set g_uThreadFinished to 0 in DLL_PROCESS_ATTACH, set to 1 when you're finished
STDAPI DllCanUnloadNow(void)
{
    do 
    {
        Sleep(1);
    } while (g_uThreadFinished == 0);
    return S_OK;
}

A full PoC proxy COM server is available at https://github.com/leoloobeek/COMProxy. It includes some code that will dynamically find the COM server that its hijacking and proxy the DllGetClassObject call. I also have a test COM client that will load WScript.Shell, run calc, and call the COM cleanup Windows API calls.

Operationalize COM Hijacking

When evaluating techniques to see where they fit in an offensive operation, its important to understand the limitations and detection points. Going through the work to proxy the legitimate COM server was important to ensure reliability on successful hijacks.

The technique does require dropping a DLL to disk which may push people away. Frankly, in this era which includes better in-memory optics, I’m open to dropping files to disk again as long as I understand the endpoint controls and feel comfortable with the file. Also, maybe you don’t have to drop a .dll to disk (hint, hint). This does present an interesting scenario: you set a COM hijack and laid down your COM server for persistence. While it wasn’t detected initially, it eventually gets scrubbed for $Reason. At this point, since the rogue DLL doesn’t exist but the registry key is still set, the legitimate DLL will NOT be loaded and this will impact the target user or system. Unfortunately, the COM subsystem does not check HKLM if the COM server in HKCU doesn’t exist.

Given this issue, hijacking COM and proxying the original object for stability might be best leveraged for execution. A few execution scenarios may be migrating to another process or lateral movement. Persistence with COM hijacking may be best for abandoned keys or the scheduled task handler hijack outlined by @enigma0x3 (listed in previous work).

Additionally, detecting COM hijacking via registry modifications is straight forward. In fact, the popular @SwiftOnSecurity Sysmon config has a rule exactly for COM hijacking here. There’s not many subkeys in  HKEY_CURRENT_USER\Software\Classes\CLSID which will make new keys added maybe appear suspicious. With that said, armed with this proxying technique, overwriting key values to COM servers is an option which may blend in. For example, Microsoft Teams starts on boot and also registers a COM class in HKCU: {CB965DF1-B8EA-49C7-BDAD-5457FDC1BF92}. There are hundreds of CLSID keys in HKLM, overwriting those may also blend in better, although they’re owned by TrustedInstaller.

With all this said, there’s not much to be said around automated detection from NGAV for COM object hijacking. In other words, detection relies on the blue team and tuning of their tools. As blue team training is the main overall objective of most red team operations, this technique may be a good training opportunity for the defenders.

Conclusion

That wraps COM hijacking for me. Feel free to reach out to me on Twitter if you have any questions or feedback on this technique. Also, at 10am Saturday, September 7 at DerbyCon a very smart dude David Tulis (@kafkaesqu3) will cover more around COM hijacking with his presentation!