Revisiting COM Hijacking
May 28 2025
By: Antero Guy • 7 min read
TL;DR: This post shows how COM hijacking can serve as a reliable persistence method while also enabling execution within commonly used applications across an environment.
Overview
Persistence is one of the most important steps in any red team engagement. Without persistence, all it takes is a reboot, a user logout, or some unexpected disruption, and boom… access gone. And let’s be real; few things are worse than losing that initial foothold.
In this blog, I’m going to walk through a persistence technique I use frequently during red team operations: Component Object Model (COM) hijacking. It’s a method that strikes a good balance between stealth and reliability and, as a bonus, you can use it for more than just persistence. I’ll show how I identify opportunities for this technique, as well as how you can use it to load a callback into a process of interest such as Chrome or Edge.
COM Hijacking Recap
If you’re unfamiliar with the inner workings of COM hijacking, MDSec and Pentestlab have great articles on the topic. But here’s the short version:
COM hijacking takes advantage of how Windows looks up and loads COM objects. Each COM class has a unique CLSID and a registry key like InProcServer32 (for DLLs) or LocalServer32 (for EXEs) that tells Windows what to load. These entries can exist in either the HKEY_LOCAL_MACHINE (HKLM) (system-wide) or HKEY_CURRENT_USER (HKCU) (user-specific) registry hives. Because of the registry search order in Windows, the HKCU hive is checked before HKLM, so if a CLSID exists in both, the one in HKCU is prioritized. Since users can write to their own HKCU hive, an attacker can create or override a CLSID entry there. If a program tries to use that COM object, Windows will load the attacker’s DLL instead of the legitimate one. So, the goal is to find a COM object that:
- Exists in HKLM
- A user-mode process uses
- Preferably has no corresponding entry in HKCU
Discovery Phase
When looking for COM hijack opportunities during an engagement, I typically focus on custom or third-party applications that run at user logon (e.g., applications in the Startup folder, scheduled tasks, registry run keys) as these are great places to find processes that can trigger a hijack without requiring user interaction.
In a previous engagement, I noticed a .lnk file in a user’s Startup folder that launched Citrix Workspace. I spun up a Windows 11 VM, downloaded Citrix Workspace, launched it, and then used System Informer to view the full process tree (As I typically include each relevant process in my Procmon filter). Executing the .lnk launched SelfService.exe, which then spawned a few child processes to include msedgewebview2.exe.
To identify potential COM servers with missing CLSIDs in the HKCU hive, I configured Process Monitor with the following filters:
- Operation: RegOpenKey
- Path ends with: InProcServer32
- Result: NAME NOT FOUND
I also included each process name as a filter parameter. The image below shows a full view of my Procmon filters.

Launching Citrix with these filters revealed a few registry events related to missing COM class objects within the HKCU registry hive. The ones that stood out were the missing CLSID references linked to msedgewebview2.exe, a process commonly spawned by other applications besides Citrix, such as Chrome, Edge, Microsoft Teams, and other M365 apps. This means we may not necessarily need Citrix on the host to trigger our persistence.
I chose one of the missing CLSIDs for deeper analysis (i.e., {54E211B6–3650–4F75–8334–FA359598E1C5}). A quick lookup in the HKLM registry hive revealed it was configured with its InProcServer32 entry pointing to C:\Windows\System32\directmanipulation.dll, along with its specified threading model.
Note: The threading model we create under HKCU should match the one defined in HKLM.
Next, I prepared the persistence payload before recreating the CLSID under the user’s HKCU registry key. This involved creating a stub DLL with the following requirements:
- Create a mutex: A mutex is needed to prevent multiple instances of the payload from executing
- Launch the payload: This could involve injecting shellcode, loading another DLL, or simply starting a process. For this demo, I’ll just have it launch calc.exe
- Proxy necessary DLL exports: We will need to forward export calls to the exports of the original DLL to preserve application stability and avoid breaking any functionality
To handle the mutex, I implemented an IsPayloadRunning
function, which uses the CreateEvent
API to define a named global event. This serves as a simple check: if the event already exists, the stub will not launch the payload. If the event does not exist, the stub will use the CreateProcessA
API to launch the payload (i.e., calc.exe).
Note: If you’re planning to inject into the calling process instead of spawning a new one, you need to use shellcode injection or call the LoadLibrary
API to load a secondary DLL.
Now we need to export the expected functions from our stub DLL and forward those calls to the corresponding functions in the legitimate directmanipulation.dll. This ensures that the application using the COM object continues to function normally while our payload executes in the background. A tool like FaceDancer can help automate the creation of these proxy definitions by parsing the export table of the target DLL.
Once generated, we can embed these proxy definitions directly into our Visual Studio project using #pragma comment(linker,…)
directives, which instruct the linker to forward exported function calls.
Note: Another tool you could use to forward exported function calls is Koppeling.
Now that our DLL is ready, the next step is to create the same CLSID under the HKCU registry hive, using the same threading model as the original; however, this time we point to the InProcServer32 default value to our stub DLL. For the purposes of this demo, the stub DLL is StubDLL.dll and located within the C:\StubDLL directory.
When Citrix Workspace launches and instantiates that COM object, Windows checks HKCU first, finds our malicious entry, and launches the calculator application.
Pivoting Into Browser Processes
After analyzing the COM object with Procmon, I noticed that several other apps (i.e., Edge, Chrome, Microsoft Teams, and OneDrive) also interacted with it.
This opened the door to getting a callback loaded into browser processes, which is especially useful for hiding HTTPS callback traffic and for cookie dumping.
Now this is where things get really cool with COM hijacking. Let’s continue with browsers as an example. Starting with version 127, Chromium-based browsers like Edge and Chrome use an app-bound encryption key to encrypt cookies. Decrypting it requires your payload to either run from the browser’s application directory or run with NT AUTHORITY\SYSTEM-level privileges. From a user-mode perspective, this makes cookie dumping much more difficult. While direct injection into Chrome is one option, it’s noisy and more likely to trigger detection. So instead of going through all this trouble, why not get a callback loaded directly into the browser process through our COM hijack?
Since multiple applications instantiate this COM object, we need to make sure our payload only runs in the processes we actually care about. To handle that, I added a check in the DLL using a function called IsChromeOrEdge
, which restricts execution to only continue if the current process is chrome.exe or msedge.exe. This check can be placed at the top of the code. If the process matches, we move on to the mutex check and then launch the payload. If not, the DLL exits quietly to avoid executing from an undesired application.
After making this change, I launched Citrix Workspace and confirmed that calc.exe did not run. However, as soon as I launched Edge or Chrome, calc.exe popped, confirming a successful hijack and code execution. From here you could use a tool such as cookie-monster-bof (Shoutout to KingOfTheNOPs for his research and tool development in this area) to dump and decrypt the browser cookies.
Final Thoughts
COM hijacking has been a dependable technique that I’ve used successfully across multiple operations without triggering an alert. It’s a flexible way to both establish persistence and gain execution inside specific applications, like Chrome, with minimal noise. Hopefully, this post showed how practical and effective COM hijacking can be when applied in the right context.