Uncovering Malicious Windows Service Installation via Event ID 7045 and Defender KQL

Catch lateral movement and persistence tools (PsExec, Cobalt Strike PsExec service) by monitoring new Windows Service installations with KQL.

Dikshant Lather
2 min read ·
Uncovering Malicious Windows Service Installation via Event ID 7045 and Defender KQL

Uncovering Malicious Windows Service Installation via Event ID 7045 and Defender KQL

Creating a Windows Service with SYSTEM privileges is a classic persistence and lateral movement technique. Frameworks like Cobalt Strike, Impacket (psexec.py), and Sysinternals psexec install temporary services to execute payloads with the highest local authority.

Whenever a service is installed on Windows, System Event ID 7045 is logged.


Sentinel KQL Query: Event ID 7045

// Detect Suspicious Windows Service Installations via Event 7045
let timeWindow = 7d;
SecurityEvent
| where TimeGenerated >= ago(timeWindow)
| where EventID == 7045
| parse EventData with * '<Data Name="ServiceName">' ServiceName '</Data>' *
| parse EventData with * '<Data Name="ImagePath">' ImagePath '</Data>' *
| parse EventData with * '<Data Name="ServiceType">' ServiceType '</Data>' *
| parse EventData with * '<Data Name="StartType">' StartType '</Data>' *
| parse EventData with * '<Data Name="AccountName">' ServiceAccount '</Data>' *
| where ImagePath has_any ("cmd.exe", "powershell.exe", "wscript.exe", "rundll32.exe", "certutil.exe", "net.exe")
    or ImagePath has_any ("\\Temp\\", "\\Users\\Public\\", "\\PerfLogs\\", "\\ADMIN$\\")
    or ServiceName matches regex @"^[A-Za-z0-9]{7,8}$" // Cobalt Strike randomized service names
| project TimeGenerated, Computer, ServiceName, ImagePath, ServiceType, StartType, ServiceAccount
| order by TimeGenerated desc

Defender for Endpoint Query (DeviceEvents)

If you rely on Defender for Endpoint rather than Event Logs:

DeviceEvents
| where TimeGenerated >= ago(7d)
| where ActionType == "ServiceInstalled"
| extend ParsedFields = parse_json(AdditionalFields)
| extend ServiceName = tostring(ParsedFields.ServiceName),
         ServicePath = tostring(ParsedFields.ServiceImagePath)
| where ServicePath has_any ("cmd", "powershell", "rundll32", "Temp", "Public")
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, ServiceName, ServicePath

Critical Patterns to Watch

  • Temporary Network Shares: Any service image path referencing ADMIN$\<filename>.exe or C$\<filename>.exe is strong evidence of remote PsExec-style lateral movement.
  • Randomized Service Names: Default Cobalt Strike and Metasploit modules generate pseudorandom 7-character string service names.

MITRE ATT&CK Mapping

  • Tactic: Persistence (TA0003), Privilege Escalation (TA0004), Lateral Movement (TA0008)
  • Technique: Create or Modify System Process: Windows Service (T1543.003)
Dikshant Lather
Written by

Dikshant Lather

Cyber Security & AI Architect

Responses (0)

Join the technical conversation or share implementation thoughts.

What are your thoughts?

Sign in to join the technical discussion or share feedback.

There are currently no responses for this story. Be the first to respond.