Impacket Deep Dive Part 6: The Ghost Shells — wmiexec, smbexec, and atexec

Every prior post in this series ends the same way: a credential, obtained without cracking anything expensive, or cracked offline from something intercepted or coerced. This post is what happens next — turning that credential into an actual remote shell on FS01 or WKS01. Impacket ships four scripts that do this, each abusing a different legitimate Windows remote-management protocol, and the differences between them are almost entirely about how much forensic evidence they leave behind, not how effective they are at getting a command to run.

The baseline: how classic PsExec actually works

Before the Impacket-native tools, it’s worth being precise about what psexec.py — Impacket’s own re-implementation of the classic SysInternals technique — actually does, because it’s the baseline every other tool in this post is quietly designed to avoid: it copies a small service binary (Impacket ships its own, RemComSvc) to the target’s ADMIN$ share, uses the Service Control Manager (SCM) over RPC to install and start it as a Windows service, and that service executes the requested command and pipes output back over a named pipe.

usage: psexec.py [-h] [-c pathname] [-path PATH] [-file FILE] [-ts] [-debug]
                 ...
                 target [command ...]

PSEXEC like functionality example using RemComSvc.

That’s an executable dropped to disk and a service created — Event ID 7045 (A service was installed in the system) fires every single time, with a service name and binary path that’s very often obviously anomalous (RemComSvc, or something similarly generic-looking). It’s the loudest option in this post by a wide margin, and modern EDR products treat a new service creation followed immediately by execution as close to a canonical red flag.

wmiexec.py — no service, no binary, no disk write

wmiexec.py takes a completely different path: Windows Management Instrumentation already exposes a method, Win32_Process.Create, that any sufficiently-privileged principal can call over DCOM to start a process on a remote host. wmiexec.py calls it directly. No service gets installed, no executable gets copied — the command runs, and output is captured by redirecting it to a temporary file on the existing ADMIN$ share (or suppressed entirely with -nooutput, which drops even that).

usage: wmiexec.py [-h] [-share SHARE] [-nooutput] [-ts] [-silentcommand]
                  [-debug] [-codec CODEC] [-shell-type {cmd,powershell}]
                  [-com-version MAJOR_VERSION:MINOR_VERSION]
                  ...
                  target [command ...]

Executes a semi-interactive shell using Windows Management Instrumentation.

Against FS01, using svc-backup’s cracked password from Part 4:

$ wmiexec.py CONTOSO/svc-backup:'B4ckup$SQL2024'@10.10.30.20
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies

[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\Windows\system32> whoami
contoso\svc-backup

C:\Windows\system32> hostname
FS01

“Semi-interactive” because there’s no real persistent shell process — every command typed here is actually a fresh Win32_Process.Create call under the hood, its output redirected to a share and read back, then the temp file deleted. It feels like a shell. It isn’t one, structurally, and that distinction is exactly what makes it so quiet: there’s no new service (no 7045), no dropped binary, and the process creation itself shows up as WmiPrvSE.exe spawning cmd.exe — which is indistinguishable, at the process-tree level, from completely legitimate remote administration via WMI, something real sysadmin tooling does constantly. Multiple detection vendors describe wmiexec.py as the hardest of the four to reliably catch with behavioral rules alone, for exactly this reason.

smbexec.py — a service, but no binary on disk

smbexec.py splits the difference: it still uses the Service Control Manager, so it still fires Event 7045, but instead of dropping a persistent executable, it creates a service whose binary path is cmd.exe itself, configured to execute a one-line command and pipe output to a file, then deletes the service immediately after:

usage: smbexec.py [-h] [-share SHARE] [-mode {SERVER,SHARE}] [-ts] [-debug]
                  [-codec CODEC] [-shell-type {cmd,powershell}]
                  ...
                  [-service-name service_name]
                  target
$ smbexec.py CONTOSO/svc-backup:'B4ckup$SQL2024'@10.10.30.20
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies

[!] Launching semi-interactive shell - Careful what you execute
C:\> whoami
contoso\svc-backup

Every command in that shell creates, runs, and destroys a new service under a randomized name — visible as a burst of 7045 / 7036 (service installed / service entered running state) event pairs, one per command typed, which is a distinctive enough cadence that it’s one of the more reliably-signatured techniques in this post despite never touching disk with a real binary. -service-name lets an operator pin a fixed, less-obviously-random name instead, trading one detection surface for a different one (a static, reused service name is its own kind of anomaly once a defender knows to look for it).

atexec.py — the loudest of the “fileless” three

atexec.py uses the Task Scheduler RPC interface (the same one behind the deprecated at.exe) to register a scheduled task that runs the command once, immediately, then deletes itself:

usage: atexec.py [-h] [-session-id SESSION_ID] [-ts] [-silentcommand] [-debug]
                 [-codec CODEC] [-hashes LMHASH:NTHASH] [-no-pass] [-k]
                 [-aesKey hex key] [-dc-ip ip address] [-keytab KEYTAB]
                 target [command ...]
$ atexec.py CONTOSO/svc-backup:'B4ckup$SQL2024'@10.10.30.20 "whoami"
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies

[!] Command not received!
[*] Attempting to create ATSVC session at 10.10.30.20
[*] Creating task \IHTKzXsu
[*] Task registered
[*] Running task
[*] Deleting task

contoso\svc-backup

Every task creation via this path is Event ID 4698 (A scheduled task was created) — an event most estates already log for entirely unrelated reasons (legitimate scheduled task creation is common), which makes it a slightly noisier haystack to search but not a hard one, since the task names Impacket generates (short, random alphanumeric strings like IHTKzXsu above) are themselves a recognizable pattern. Of the three “no binary on disk” tools in this post, atexec.py is generally considered the easiest to catch, precisely because Task Scheduler activity has better default audit coverage than WMI process creation does in most environments.

dcomexec.py — same idea, different DCOM object

dcomexec.py rounds out the set, using various DCOM application objects (MMC20.Application, ShellWindows, ShellBrowserWindow) instead of WMI’s Win32_Process — a lateral-movement technique first popularized independently of Impacket and later folded into the toolkit. Functionally it sits alongside wmiexec.py in terms of stealth: no service, no dropped binary, DCOM activity that can look legitimate depending on the environment’s baseline.

Detection at a glance

ToolMechanismPrimary eventDisk artifact
psexec.pySCM + dropped service binary7045 (persistent, obvious name)Yes — binary on ADMIN$
smbexec.pySCM + cmd.exe-as-service, self-deleting7045/7036 burst, one pair per commandNo
atexec.pyTask Scheduler RPC4698, short-lived random task nameNo
wmiexec.pyWMI Win32_Process.CreateWMI-Activity operational log; WmiPrvSE.exe spawning cmd.exeNo
dcomexec.pyDCOM application objectsDCOM/COM+ launch events, less commonly monitoredNo

The pattern worth internalizing: stealth correlates with how well-instrumented the underlying protocol is by default, not with how “advanced” the technique is. WMI and DCOM are old, broadly used for legitimate management, and comparatively under-logged out of the box. Task Scheduler and the Service Control Manager are both extremely well covered by default Windows auditing, which is precisely why psexec.py and atexec.py are the two easiest tools in this post to catch, and wmiexec.py is the one every serious detection engineering team specifically calls out as the hard case.

It’s the same lesson the AI Part 7 post landed on from a completely different angle — an LLM given run_exploit and send_session_command tools against Metasploitable2 wasn’t dangerous because of anything exotic in the exploit itself, it was dangerous because it was fast, willing, and (for the smaller models) an unreliable narrator of what it had actually done. The tooling in this post is the same story again: nothing here is a zero-day, everything is a documented Windows management feature, and the entire defensive posture comes down to whether anyone is watching the plumbing.

Defensive notes

Enable and forward the WMI-Activity operational log. Microsoft-Windows-WMI-Activity/Operational captures WMI method invocations that the classic Security event log doesn’t, and it’s the single best lever against wmiexec.py specifically — most environments don’t collect it by default, which is a large part of why this tool has the reputation it does.

Alert on service creation with short-lived, generic, or cmd.exe-as-binary services. A 7045 where the binary path is cmd.exe or %COMSPEC% with a /Q /c echo ... > \\...\ADMIN$\...-style command line is close to a direct signature for smbexec.py, and Splunk’s public security content for Impacket ships this exact detection.

Baseline legitimate scheduled task creation before alerting on 4698 broadly. Too noisy to alert on unconditionally in most estates, but a short random-string task name created and deleted within seconds, especially from a source that isn’t a known management server, is a strong atexec.py signal once tuned.

Restrict and monitor ADMIN$/C$ access from workstation-to-workstation. None of these four tools work without administrative access to the target’s default shares — Windows Firewall rules or a tiered administration model (blocking workstation-to-workstation and workstation-to-server lateral movement paths that have no legitimate business reason to exist) closes off the precondition rather than trying to catch each tool individually.

Next: Part 7, where a shell on FS01 turns into the credential material for the entire domain — secretsdump.py, SAM, LSA secrets, and DCSync.