Why this matters now
Distributed Component Object Model (DCOM) is one of Windows' oldest and quietest remote-execution channels. It exists so that a program on one machine can activate and drive software objects on another — a feature management tooling and enterprise applications depend on daily. Attackers depend on it too. Tools like Impacket's dcomexec, the Invoke-DCOM PowerShell module, and SharpLateral all use legitimate DCOM activation to run commands on remote hosts, which means no new service is installed, no scheduled task appears, and no PsExec-style binary ever lands on disk. MITRE tracks this as T1021.003 — Remote Services: Distributed Component Object Model, and it is a favorite for one reason: the logon it leaves behind looks like ordinary Windows plumbing.
How attackers move laterally over DCOM
The technique is remote object activation. From a compromised host, the attacker asks the target's DCOM service to instantiate a COM class — for example MMC20.Application, ShellWindows, or an Office application object — and then calls a method on it that executes a command (launching a script via the MMC snap-in, running a macro through the Office object, or spawning a shell through the WMI provider over DCOM). The command executes on the target under the attacker's credentials, and the spawned process's parent is dllhost.exe, the DCOM surrogate host — a completely legitimate parent. On the target's Security log this surfaces as Event 4624 with Logon Type 3 (network logon), the same type a file share access or management tool would produce. Nothing in that single event says 'remote code execution'; the story is in the combination of the network logon, the DCOM activation noise, and whatever dllhost.exe ends up parenting.
The KQL hunts
All queries target Microsoft Defender's DeviceLogonEvents and DeviceProcessEvents tables. Tune the lookback to your retention.
Hunt 1 — Network logons sprayed across many hosts
DCOM lateral movement produces Type 3 network logons from the attacker's host to each target. An account logging on to dozens of hosts in a short window is the classic lateral-movement shape.
DeviceLogonEvents
| where TimeGenerated > ago(7d)
| where LogonType == "3"
| where isnotempty(RemoteIP)
| summarize HostCount = dcount(DeviceName), Hosts = make_set(DeviceName, 25),
FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
by AccountName, RemoteIP
| where HostCount > 5
| order by HostCount desc
Hunt 2 — Anything parented by dllhost.exe that should not be
DCOM-activated code runs under the surrogate host dllhost.exe. Normal children are other COM servers; shells, Office apps launched remotely, and management consoles are not.
DeviceProcessEvents
| where TimeGenerated > ago(30d)
| where InitiatingProcessFileName =~ "dllhost.exe"
| where FileName in~ ("mmc.exe", "powershell.exe", "pwsh.exe", "cmd.exe",
"wscript.exe", "cscript.exe", "excel.exe", "winword.exe",
"rundll32.exe", "regsvr32.exe", "mshta.exe")
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine,
InitiatingProcessCommandLine, InitiatingProcessAccountName
| order by TimeGenerated desc
Hunt 3 — Network logon followed by a dllhost-spawned process
Join the two halves of the technique: a Type 3 logon on a host, then within minutes a suspicious child of dllhost.exe on the same host. Either signal alone is noisy; together they are the technique.
let logons = DeviceLogonEvents
| where TimeGenerated > ago(7d)
| where LogonType == "3"
| where isnotempty(RemoteIP)
| project LogonTime = TimeGenerated, DeviceName, AccountName, RemoteIP;
let launches = DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName =~ "dllhost.exe"
| where FileName in~ ("mmc.exe", "powershell.exe", "pwsh.exe", "cmd.exe",
"wscript.exe", "cscript.exe", "excel.exe", "winword.exe")
| project LaunchTime = TimeGenerated, DeviceName, FileName, ProcessCommandLine;
logons
| join kind=inner launches on DeviceName
| where LaunchTime between (LogonTime .. (LogonTime + 15m))
| project LogonTime, LaunchTime, DeviceName, AccountName, RemoteIP,
FileName, ProcessCommandLine
| order by LogonTime desc
Hunt 4 — First-seen remote DCOM initiators
Baseline which hosts ever initiate DCOM-style network logons in your estate; the attacker's beachhead usually shows up as a first-seen initiator.
DeviceLogonEvents
| where TimeGenerated > ago(30d)
| where LogonType == "3"
| where isnotempty(RemoteIP)
| summarize FirstSeen = min(TimeGenerated), Targets = dcount(DeviceName)
by InitiatorDevice = RemoteIP, AccountName
| where FirstSeen > ago(7d)
| order by FirstSeen desc
Splunk equivalents
SPL 1 — Network logons (EventCode 4624, Logon Type 3) by breadth
index=windows EventCode=4624 Logon_Type=3
| stats dc(dest) as host_count, values(dest) as targets,
min(_time) as first_seen, max(_time) as last_seen
by src_ip, user
| where host_count > 5
| sort - host_count
SPL 2 — Sysmon EventCode 1: suspicious children of dllhost.exe
index=windows EventCode=1 ParentImage="*\\dllhost.exe"
(Image="*\\mmc.exe" OR Image="*\\powershell.exe" OR Image="*\\pwsh.exe"
OR Image="*\\cmd.exe" OR Image="*\\wscript.exe" OR Image="*\\cscript.exe"
OR Image="*\\excel.exe" OR Image="*\\winword.exe"
OR Image="*\\rundll32.exe" OR Image="*\\mshta.exe")
| table _time, host, User, Image, CommandLine, ParentImage
| sort - _time
Where the System log is collected, also watch the DistributedCOM source: Event IDs 10028 (DCOM could not communicate with a remote computer) and 10036 (activation blocked by server-side authentication policy) frequently bracket DCOM probing and failed activation attempts.
Triage: what to do when a hunt hits
First, confirm the pairing on the target host: a 4624 Type 3 logon from the source IP followed within minutes by a new process whose parent is dllhost.exe. Second, identify the command: capture the full command line of the child process and the grandparent chain — mmc.exe spawned remotely via MMC20.Application is the signature of Invoke-DCOM and Impacket-style tooling. Third, scope the source: pull every other host the same source IP or account touched in the window — DCOM movement is rarely a single hop. Fourth, check the System log for DistributedCOM 10028/10036 events around the activation time to confirm the DCOM channel and catch failed attempts against other hosts. Fifth, recover the payload: any file written by the spawned process (Sysmon EventCode 11 / DeviceFileEvents) gets hashed and detonated. Finally, treat the source host as compromised until proven otherwise — the DCOM session rode in on credentials that are now attacker-controlled.
Remediation and hardening
Kill active sessions and isolate both the source and target hosts, then rotate credentials for every account involved — DCOM lateral movement is credential replay in practice, so stale hashes and tickets keep working after the box is clean. Re-image confirmed-compromised hosts. Longer term: use Component Services (dcomcnfg) to restrict remote launch and activation permissions to the accounts that genuinely need them, segment management protocols so DCOM traffic cannot traverse freely between workstation tiers, and monitor for first-seen DCOM initiators the way you monitor first-seen PsExec use. You cannot disable DCOM everywhere — too much legitimate tooling needs it — but you can make sure that when a workstation starts activating objects on fifty other workstations, someone is watching.
