>
av.Amit VijayanCYBERSECURITY & RESEARCHLet\u2019s connect
Back to the library

Threat Hunting Playbooks

Hunting PsExec Lateral Movement: Remote Services Installed Over SMB

Thumbnail for: Hunting PsExec Lateral Movement: Remote Services Installed Over SMB

Why this matters now

PsExec is one of the most abused legitimate tools in Windows intrusions. It is signed by Microsoft, ships with Sysinternals, and needs no exploit: give it an administrator credential and it runs a command on a remote host as SYSTEM. Ransomware affiliates, red teams, and intruders of every skill level use it daily because it is trusted, ubiquitous, and — critically — it cleans up after itself. On disconnect, PsExec deletes the remote service it installed. That self-deletion is exactly why this technique deserves a hunt: the window between installation and deletion is the only time the evidence exists in one place, and most defenders are not watching it.

MITRE ATT&CK tracks the technique under T1021.002 (Remote Services: SMB/Windows Admin Shares) for the lateral step and T1569.002 (System Services: Service Execution) for the execution step. The same service-installation primitive is used by Impacket's smbexec.py and psexec.py, NetExec, PAExec, and RemCom — so a hunt built on the service-install event catches the whole family, not just the one binary.

How attackers move laterally with remote services

The mechanics are straightforward. From a compromised host, the attacker connects to the target's ADMIN$ share over SMB (port 445) with an administrative credential, copies the service binary to the share, and asks the Service Control Manager to create and start a service. On the target, the System log records Event 7045 — "A service was installed in the system" — naming the service PSEXESVC with an image path under ADMIN$. The binary, psexesvc.exe, runs as SYSTEM with services.exe as its parent, then spawns whatever the attacker asked for — usually cmd.exe or powershell.exe. When the session ends, the service is deleted.

The telemetry a defender can count on: a Type 3 network logon (Event 4624) from the source host, a detailed file-share access event (Event 5145) for the ADMIN$ share, the 7045 service-install event with the suspicious name and image path, and process-creation events (Event 4688 or Sysmon Event 1) showing psexesvc.exe parented by services.exe and parenting a shell. Impacket-based tooling randomizes the service name, so name-matching alone is not enough — the image path under ADMIN$ and the services.exe parent-child relationship are the durable signals.

The KQL hunts

All queries target Microsoft Defender's DeviceEvents and DeviceProcessEvents tables. Tune the lookback to your retention.

Hunt 1 — PSEXESVC anywhere in process telemetry

The simplest high-fidelity hunt: the PsExec service binary should not appear at all unless someone intentionally uses the tool.

DeviceProcessEvents
| where TimeGenerated > ago(30d)
| where FileName =~ "psexesvc.exe" or InitiatingProcessFileName =~ "psexesvc.exe"
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine,
          InitiatingProcessFileName, InitiatingProcessCommandLine
| order by TimeGenerated desc

Hunt 2 — Unusual children of services.exe

Legitimate services.exe children are overwhelmingly svchost.exe. Anything else parented by the Service Control Manager host deserves a look — this catches randomized Impacket service names that Hunt 1 would miss.

DeviceProcessEvents
| where TimeGenerated > ago(30d)
| where InitiatingProcessFileName =~ "services.exe"
| where FileName !in~ ("svchost.exe")
| summarize Hosts = dcount(DeviceName), FirstSeen = min(TimeGenerated),
            Examples = make_set(ProcessCommandLine, 5)
    by FileName, InitiatingProcessAccountName
| where Hosts > 1 or FileName in~ ("psexesvc.exe", "paexec.exe",
    "remcomsvc.exe", "cmd.exe", "powershell.exe", "pwsh.exe")
| order by FirstSeen desc

Hunt 3 — Service installs with network-sourced image paths (Event 7045)

Event 7045 is the anchor of this hunt. In Defender XDR it surfaces in DeviceEvents with ActionType ServiceInstalled. Filter for service names and image paths that point back to an administrative share.

DeviceEvents
| where TimeGenerated > ago(30d)
| where ActionType == "ServiceInstalled"
| extend Details = parse_json(AdditionalFields)
| extend ServiceName = tostring(Details.ServiceName),
         ImagePath = tostring(Details.ImagePath)
| where ServiceName =~ "PSEXESVC" or ImagePath has "ADMIN$"
    or ImagePath has "PSEXESVC" or ServiceName has "remcom"
    or ServiceName has "paexec"
| project TimeGenerated, DeviceName, AccountName, ServiceName, ImagePath,
          InitiatingProcessAccountName
| order by TimeGenerated desc

Note: the exact keys inside AdditionalFields can vary by sensor version — if the parse returns empty, project the raw AdditionalFields column for one row and adjust the key names.

Hunt 4 — One account installing services across many hosts

Lateral movement is a breadth behavior. An account creating services on several hosts in a week is the attacker-shaped signal, regardless of the service name.

DeviceEvents
| where TimeGenerated > ago(7d)
| where ActionType == "ServiceInstalled"
| summarize HostCount = dcount(DeviceName), Hosts = make_set(DeviceName, 25),
            FirstSeen = min(TimeGenerated)
    by AccountName
| where HostCount > 3
| order by HostCount desc

Splunk equivalents

Event 7045 lives in the System log (Service Control Manager), not the Security log — point your search at WinEventLog:System.

SPL 1 — Event 7045 with suspicious service names or paths

index=wineventlog EventCode=7045
| table _time, host, Account_Name, Service_Name, ImagePath
| search Service_Name="PSEXESVC" OR ImagePath="*ADMIN$*" OR ImagePath="*PSEXESVC*"
  OR Service_Name="*remcom*" OR Service_Name="*paexec*"

SPL 2 — psexesvc.exe in process-creation telemetry

index=wineventlog (EventCode=4688 OR EventCode=1)
  (New_Process_Name="*psexesvc.exe" OR Parent_Image="*psexesvc.exe")
| table _time, host, Account_Name, New_Process_Name, CommandLine, Parent_Image

SPL 3 — Service installs by breadth

index=wineventlog EventCode=7045
| stats dc(host) as host_count, values(host) as hosts, earliest(_time) as first_seen by Account_Name
| where host_count > 3
| sort - host_count

Triage: what to do when a hunt hits

  1. Scope first. Pull every 7045 on the target host and every host the source account touched in the last 7 days. One remote-service install is rarely the only one.
  2. Read the process tree. The expected attacker chain is services.exe → psexesvc.exe → cmd.exe/powershell.exe. Capture the full command lines — they reveal the attacker's intent (credential theft, discovery, payload staging).
  3. Verify the source. Correlate with Event 4624 Type 3 network logons on the target: the source host and timestamp should line up with the 7045. If the source is a workstation that an admin does not manage from, treat the source as compromised too.
  4. Check for persistence and fallout. PsExec normally deletes its service, but verify with sc \\target query. Look for credential material the attacker may have touched: dumped SAM/NTDS, new accounts, scheduled tasks created in the same window.
  5. Collect artifacts. Grab the Prefetch file for PSEXESVC.EXE-*.pf, Amcache entries, and the ADMIN$-copied binary if it survives. Then isolate both hosts before the attacker pivots further.

Remediation and hardening

  • Block the known remote-execution service binaries by hash and name in your EDR: psexesvc.exe, PAExec, and RemCom variants.
  • Restrict SMB (TCP 445) workstation-to-workstation in host firewalls — lateral movement over admin shares dies when workstations cannot reach each other's shares.
  • Deploy LAPS and tiered administration so one compromised credential cannot roam the whole estate; do admin work from privileged access workstations, not daily-driver machines.
  • Enable SMB signing to raise the cost of relay-style attacks that harvest the admin credential PsExec needs.
  • Baseline Event 7045 in your SIEM and alert on new service names and image paths, not just the known-bad ones — Impacket's randomized names only stand out against a baseline.

Keep exploring.

All articles