In this very easy Sherlock, you will learn how to detect NTDS.dit dumping which is one of the most critical Active directory attacks. You will get your hands on event logs to respond to an attack where the attacker utilized ntdsutil utility to dump the NTDS.dit database.
An attacker with an Administrator session on a domain controller used ntdsutil.exe to copy the Active Directory database to disk. The tool first enumerated the privileged local groups to confirm the account could take a backup, then started the Volume Shadow Copy service to snapshot the volume. The ESENT provider recorded the copy being created in a staging folder under Windows\Temp and then detached, so the whole dump took a few seconds. The logs hold no logon events, so the start of the malicious session was reconstructed from the Kerberos ticket request and the first event carrying its logon ID.
evidence/Artifacts/*.evtx (System, Security, Application) → parse with EvtxECmd, then filter the CSV with Pythonwork/evtx/all.csv → one CSV for every log, so everything can be filtered by Provider, EventId and Computer in one placeTimestamp note: EvtxECmd writes UTC. Every time question wants
YYYY-MM-DD HH:MM:SS, so drop the fractional seconds.
Tip: ESENT payloads are long. Select only the time, provider, event ID and message columns early, or the output is unreadable.
EvtxECmd -d evidence/Artifacts --csv work/evtx --csvf all.csv
The pivot is the logon ID 0x8DE3D from the ntdsutil.exe events in Q6. The obvious source is Security event 4624 matched on TargetLogonId, but SECURITY.evtx holds no 4624 events at all, so there is no logon record to read. Searching every event for the logon ID shows its first appearance (a 5379 credential read) at 05:36:31, a fraction of a second after an Administrator Kerberos TGT request (4768) at the same second. That timestamp was reasoned from the earliest event carrying the ID and the TGT request, not read from a 4624, and it is the value HTB accepted.
python3 -I -c "import csv;[print(r['TimeCreated'],r['EventId'],r['Provider']) for r in csv.DictReader(open('work/evtx/all.csv')) if '0x8DE3D' in str(r.values())]"
Gotcha: An empty 4624 search does not mean the filter is wrong. The log simply has no logon events, so check that before spending time on field names.
Security event 4799 (a security-enabled local group was enumerated) is raised with ntdsutil.exe as the calling process. Filter 4799 to that caller around the dump window. Two groups come back, the ones that grant backup rights. The same events give the process ID 0xF64 and the logon ID 0x8DE3D used for Q7.
python3 -I -c "import csv;[print(r['TimeCreated'],r['PayloadData1'],r['PayloadData2']) for r in csv.DictReader(open('work/evtx/all.csv')) if r['EventId']=='4799' and 'ntdsutil' in str(r.values())]"
ntdsutil snapshots the volume through the Volume Shadow Copy service, so System event 7036 for that service entering the running state marks the start. It appears twice in the logs. The earlier one stands alone; the later one is followed within a second by the database events in Q2 and Q3, so it is the dump.
python3 -I -c "import csv;[print(r['TimeCreated'],r['PayloadData1']) for r in csv.DictReader(open('work/evtx/all.csv')) if r['EventId']=='7036' and 'Shadow' in str(r.values())]"
Gotcha: The question asks for the most recent entry, not the first. Taking the earlier 7036 gives the wrong time.
The database lifecycle events following the shadow copy start (325, 326 and 327) all share one provider. Listing the providers of those event IDs shows it immediately. In the CSV it is the Provider column of those rows.
python3 -I -c "import csv;print({r['Provider'] for r in csv.DictReader(open('work/evtx/all.csv')) if r['EventId'] in ('325','326','327')})"
From Q5 the source is ESENT, so read its events in the minute after the shadow copy started. Event 325 reports a new database being created, and its message carries the full path of the copy. The folder sits under C:\Windows\Temp, which is not where a domain controller keeps its live database and is a staging location on its own.
python3 -I -c "import csv;[print(r['TimeCreated'],r['EventId'],r['MapDescription'],r['PayloadData1']) for r in csv.DictReader(open('work/evtx/all.csv')) if r['Provider']=='ESENT' and r['TimeCreated'].startswith('2024-05-15 05:39')]"
Same ESENT listing as Q2. The timestamp of the event 325 row, the one that names the new database path, is the creation time. It lands one second after the shadow copy started in Q1.
python3 -I -c "import csv;[print(r['TimeCreated'],r['EventId']) for r in csv.DictReader(open('work/evtx/all.csv')) if r['Provider']=='ESENT' and r['EventId']=='325']"
A database that has been detached is finished and closed, so the completion marker is the ESENT event 327 for the same database, not another 325. It follows the creation event by a couple of seconds, which is the length of the whole dump.
python3 -I -c "import csv;[print(r['TimeCreated'],r['EventId']) for r in csv.DictReader(open('work/evtx/all.csv')) if r['Provider']=='ESENT' and r['EventId']=='327']"
| Phase | Action |
|---|---|
| Initial Access | Administrator session (logon ID 0x8DE3D) established on the domain controller |
| Discovery | ntdsutil.exe enumerates the Administrators and Backup Operators groups |
| Credential Access | Volume Shadow Copy service starts to snapshot the volume |
| Credential Access | ESENT creates the database copy under C:\Windows\Temp\dump_tmp |
| Credential Access | ESENT detaches the copy, marking the dump complete |
| Timestamp (UTC) | Event |
|---|---|
| 2024-05-15 05:35:29 | Earlier Volume Shadow Copy service start, unrelated to the dump |
| 2024-05-15 05:36:31 | Administrator Kerberos TGT request, then first event carrying logon ID 0x8DE3D |
| 2024-05-15 05:39:55 | Volume Shadow Copy service enters the running state for the dump |
| 2024-05-15 05:39:56 | ESENT creates the new database in dump_tmp |
| 2024-05-15 05:39:58 | ESENT detaches the database, dump complete |
| Type | Value |
|---|---|
| Process | ntdsutil.exe (PID 0xF64) |
| Logon ID | 0x8DE3D |
| File | C:\Windows\Temp\dump_tmp\Active Directory\ntds.dit |
| Directory | C:\Windows\Temp\dump_tmp |
| Technique | ID | Description |
|---|---|---|
| OS Credential Dumping: NTDS | T1003.003 | ntdsutil.exe copied ntds.dit to a Temp staging folder |
| Valid Accounts: Domain Accounts | T1078.002 | Administrator session running the tool |
| Permission Groups Discovery: Local Groups | T1069.001 | Enumeration of Administrators and Backup Operators |
Alert on ntdsutil.exe outside planned maintenance. A Volume Shadow Copy start followed by ESENT 325 and 327 within seconds is a tight, reliable pattern for an NTDS dump on a domain controller.
Watch ESENT database creation for ntds.dit anywhere but the default NTDS location. A copy in a Temp folder is a staging indicator by itself, and event 325 carries the full path.
Event 4799 from ntdsutil.exe is a useful secondary signal. The Administrators and Backup Operators checks fire before the copy starts, so they can give earlier warning than the file events.
Keep logon auditing enabled and retained. SECURITY.evtx here has no 4624 events, so tying the dump back to the start of its session depends on inference from a TGT request and the first event carrying the logon ID.
The attacker copied the entire Active Directory database in about three seconds using a signed Microsoft tool, and the evidence of it survives only in System, Application and a few Security events. Missing logon auditing is the gap: the dump is fully reconstructed, but its session start can only be estimated.