Adversaries may use the impacket dcomexec module to execute code remotely on Windows systems via DCOM, bypassing traditional defense mechanisms. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential lateral movement and persistence tactics early.
KQL Query
let LookupTime = 30d;
DeviceNetworkEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessFileName =~ "explorer.exe"
| where ActionType == 'InboundConnectionAccepted'
| project InboundConnTimestamp = Timestamp, DeviceName, InboundConnectionToExplorer = RemoteIP, InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessFileName =~ "explorer.exe"
| project ProcessStartTimestamp = Timestamp, DeviceName, StartedProcessCmdline = tolower(ProcessCommandLine), StartedProcessCreationTime = ProcessCreationTime, StartedProcessId = ProcessId, StartedProcessFileName = tolower(FileName), StartedProcessFolderPath = tolower(FolderPath), InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
) on DeviceName, InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
| where ProcessStartTimestamp between (InboundConnTimestamp .. (InboundConnTimestamp + 1m))
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessParentFileName =~ "explorer.exe"
|project DeviceName, ChildProcessTimestamp = Timestamp, StartedProcessCmdline = tolower(InitiatingProcessCommandLine), StartedProcessCreationTime = InitiatingProcessCreationTime, StartedProcessId = InitiatingProcessId, StartedProcessFileName = tolower(InitiatingProcessFileName), StartedProcessFolderPath = tolower(InitiatingProcessFolderPath), ChildProcessId= ProcessId, ChildProcessName = FileName, ChildProcessCommandLine = ProcessCommandLine
) on DeviceName, StartedProcessCmdline, StartedProcessCreationTime, StartedProcessId, StartedProcessFileName, StartedProcessFolderPath
| join kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessParentFileName =~ "explorer.exe"
|project DeviceName, ChildProcessTimestamp = Timestamp, StartedProcessCmdline = tolower(InitiatingProcessCommandLine), StartedProcessCreationTime = InitiatingProcessCreationTime, StartedProcessId = InitiatingProcessId, StartedProcessFileName = tolower(InitiatingProcessFileName), StartedProcessFolderPath = tolower(InitiatingProcessFolderPath), RemoteIP, RemoteUrl
) on DeviceName, StartedProcessCmdline, StartedProcessCreationTime, StartedProcessId, StartedProcessFileName, StartedProcessFolderPath
| summarize ConnectedAddresses = make_set(RemoteIP), ConnectedUrl = make_set(RemoteUrl), ChildProcesses = make_set(ChildProcessName), ChildProcessCmdlines = make_set(ChildProcessCommandLine) by DeviceName, InitiatingSourceIP = InboundConnectionToExplorer, StartedProcessCmdline, StartedProcessCreationTime, StartedProcessId, StartedProcessFileName, StartedProcessFolderPath, Timestamp = InboundConnTimestamp
id: 3069ee4c-68a2-4512-9048-4751bc0fbac2
name: detect-impacket-dcomexec
description: |
This query looks for signs of impacket dcomexec module.
Author: Jouni Mikkola
More info: https://threathunt.blog/impacket-part-2/
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- DeviceProcessEvents
tactics:
- Execution
relevantTechniques:
- T1559.001
query: |
let LookupTime = 30d;
DeviceNetworkEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessFileName =~ "explorer.exe"
| where ActionType == 'InboundConnectionAccepted'
| project InboundConnTimestamp = Timestamp, DeviceName, InboundConnectionToExplorer = RemoteIP, InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessFileName =~ "explorer.exe"
| project ProcessStartTimestamp = Timestamp, DeviceName, StartedProcessCmdline = tolower(ProcessCommandLine), StartedProcessCreationTime = ProcessCreationTime, StartedProcessId = ProcessId, StartedProcessFileName = tolower(FileName), StartedProcessFolderPath = tolower(FolderPath), InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
) on DeviceName, InitiatingProcessFileName, InitiatingProcessCreationTime, InitiatingProcessId
| where ProcessStartTimestamp between (InboundConnTimestamp .. (InboundConnTimestamp + 1m))
| join kind=leftouter (
DeviceProcessEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessParentFileName =~ "explorer.exe"
|project DeviceName, ChildProcessTimestamp = Timestamp, StartedProcessCmdline = tolower(InitiatingProcessCommandLine), StartedProcessCreationTime = InitiatingProcessCreationTime, StartedProcessId = InitiatingProcessId, StartedProcessFileName = tolower(InitiatingProcessFileName), StartedProcessFolderPath = tolower(InitiatingProcessFolderPath), ChildProcessId= ProcessId, ChildProcessName = FileName, ChildProcessCommandLine = ProcessCommandLine
) on DeviceName, StartedProcessCmdline, StartedProcessCreationTime, StartedProcessId, StartedProcessFileName, StartedProcessFolderPath
| join kind=leftouter (
DeviceNetworkEvents
| where Timestamp > ago(LookupTime)
| where InitiatingProcessParentFileName =~ "explorer.exe"
|project DeviceName, ChildProcessTimestamp = Timestamp, StartedProcessCmdline = tolower(InitiatingProcessCommandLine), StartedProcessCreationTime = InitiatingProcessCreationTime, StartedProcessId = InitiatingProcessId, StartedProcessFileName = tolower(InitiatingProcessFileName), StartedProcessFolderPath = tolower(InitiatingProcessFolderPath), RemoteIP, RemoteUrl
) on DeviceName, StartedProcessCmdline, StartedProcessCreationTime, StartedProcessId, StartedProcessFileName, StartedProcessFolderPath
| summarize ConnectedAddresses = make_set(RemoteIP), ConnectedUrl = make_set(RemoteUrl), ChildProcesses = make_set(ChildProcessName), Chi
| Sentinel Table | Notes |
|---|---|
DeviceNetworkEvents | Ensure this data connector is enabled |
DeviceProcessEvents | Ensure this data connector is enabled |
Scenario: Legitimate DCOM Execution via dcomexec for Remote Administration
Description: A system administrator uses the dcomexec module from Impacket to remotely execute administrative tasks on a Windows machine.
Filter/Exclusion: Check for known admin accounts (e.g., Administrator, svc_account) and filter by IP addresses within the internal network. Use a filter like:
process.parent_process.name == "explorer.exe" and process.name == "dcomexec" and source_ip in (internal_network_range)
Scenario: Scheduled Job Using Impacket for System Maintenance
Description: A scheduled job runs the dcomexec module as part of a regular system maintenance task, such as patching or configuration updates.
Filter/Exclusion: Filter by process owner and job name. Use a filter like:
process.owner == "system" and process.name == "dcomexec" and process.command_line contains "scheduled_job_name"
Scenario: Legitimate Use of Impacket for Active Directory Enumeration
Description: A security analyst uses Impacket’s dcomexec module to perform legitimate Active Directory enumeration or user account discovery.
Filter/Exclusion: Filter by user account and command-line arguments. Use a filter like:
process.owner == "security_analyst" and process.name == "dcomexec" and process.command_line contains "enum_users"
Scenario: Impacket Used for Internal Tooling or Automation
Description: A DevOps team uses Impacket’s dcomexec as part of an internal automation tool for configuration management or deployment.
Filter/Exclusion: Filter by known internal tools and user groups. Use a filter like: