The Case of a Stubborn ntds.dit

The awesomesauce of the Kerberos Golden Ticket (based on the spoofed-PAC whitepaper from BlackHat 2012) has started to change how I operate on my engagements, especially during repeat assessments done for the same customer. I’m now maniacally intent on getting the krbtgt hashes for as many domains as I can in the target network. Most often, I’ll try to do some trust enumeration and then target the forest root if I can realistically reach it.

Once I get to a DC, I try not to use Meterpreter’s smart_hashdump if I can help it. There is a particular defensive product that has given us heartburn when trying to use this module to dump hashes from a live domain controller. Think hash dumping failing, or even the DC rebooting :)

So my standard tradecraft is to not put any active agents on a DC itself, but to use volume shadow copy to save off clones of the SYSTEM hive and the ntds.dit Active Directory database. There are some great posts out there on how to do just this, through a RDP session, PSEXEC, or straight with WMI. This technique has been around for a good while but still works like a charm. Also, clymb3r has a method that uses PowerShell (and no Volume Shadow Copy) to clone off NTDS.dit’s. This script (Invoke-NinjaCopy) has been in PowerSploit for a bit now.

After you get these files back onto your machine, standard practice seems to involve using libesedb to extract the datatable from the ntds.dit database, and then ntdsxtract/creddump to extract the hashes (or history/whatever you want). However, I’ve had a few engagements now where the ntds.dit recovered didn’t like to parse with these libraries. The database being wonky and/or ‘not being shut down properly’ caused standard extraction to fail. Even Microsoft’s repair tool Esentutl failed to help deliver me that delicious krbtgt hash.

I may have been missing something obvious/doing something dumb that caused the extraction to error out, but my focus on a limited engagement is to get things working as quickly as I can. And there’s nothing more frustrating than going through a non-trivial attack chain, knocking over a DC, exfiltrating the ntds.dit/SYSTEM hive, and then not being able to extract the information you want from it :(

Luckily, after some research, there was one tool that seemed to work more consistently across the (possibly corrupted) ntds.dit databases I had in my possession. Impacket, the awesome network manipulation library, has a great tool called esentutl.py which will extract tables from a NTDS.dit database. However, while all the data needed is there, this output doesn’t play nice by default with the creddump toolkit. I got tired of doing the decryption more or less manually per account and ended up writing a short parser for this type of output.

ImpDump will take the raw output from esentutl.py and decrypt user hashes and/or user hash histories using the appropriate SYSTEM hive. After installing Impacket, you can save some space on the initial extract by just pulling the fields we need for hash extraction by using the supplied ./extract.sh bash script. This just wraps some syntax for esentutl.py:

./extract.sh /path/to/ntds.dit > output

Then using the SYSTEM hive of the DC and ImpDump, you can extract users hashes like so:

./impdump.py /path/to/SYSTEM output > hashes.txt

If you want hash histories, do:

./impdump.py /path/to/SYSTEM output -history > hash_history.txt

If anyone else find this useful, let me know!

9 thoughts on “The Case of a Stubborn ntds.dit”

  1. I’ve been coming across exactly the same issue recently, extracting the ntds.dit only to find that accounts for which I know the password for are not breaking out when provided with this known password.

    Whilst I’m not able to shed any light on the cause, I’ve been having great success with your solution posed in this post i.e. no hash corruptions. The only problem I have noted is that not 100% of accounts are extracted from the ntds.dit (maybe due to the corruptions being ignored in the extract?). It would be nice to get a full handle of this.

    Thanks for the post : )

    1. My guess is that there are some ESE database entries that aren’t structured “correctly”, which is possibly why other tools would fail out on parsing. Might be why those entries are being skipped, but I’m not positive.

  2. Wintermute4316

    harmj0y great post! Just to share my adventures with stubborn ntds.dit extractions, I wish I could say esentutl.py/extract.sh/impdump.py helped me in my particular case, but unfortunately for me the extract.sh/esentutl.py ran for about 8-9 hours, completed fine with no errors, but when I parsed the “output” file with imdump.py, many of the hashes extracted were not validly formed (NT hash was much too long at 48 bytes hex vs what it should be at 32 bytes hex) and the few that were validly formed, yielded failed logons with pass-the-hash auth tests. The issue might have been with with esentutl.py (latest stable version of Impacket 0.9.13) not parsing it correctly, but I can’t be sure. In my case I was working with a very large ntds.dit file (30+GB) and taken from a fully updated Win2k12 server using ntdsutil.exe/esentutil.exe method as such:

    ntdsutil.exe “activate instance ntds” “ifm” “Create Full ” quit quit
    esentutl.exe /p /o “\ntds.dit”

    That copy/repair process took about 1-2 hours to complete on the target DC (with times where ntdsutil seemed like it hung for a while, but then continued copying after a long pause) and then when completed successfully I downloaded the ntds.dit and registry hives to my system.

    I was then able to parse the ntds.dit file correctly and within a reasonable time relatively speaking (about 26-27 hours) using this specific version of libesedb v20140406. I do not recommend to use the latest versions of libesedb as they are very, very slow (same extract would have taken weeks or months on the latest versions of libesedb). The 20140406 version is not available on the Github page for the libesedb project, but I found a mirror here which works perfectly:

    https://archive-master.linux.bfh.ch/files/old/reprepro.its.bfh.ch/archive-master/releases/wheezy/packages.orig/libesedb/other/libesedb-experimental-20140406.tar.gz

    I also documented the issues I went through with newer versions of libesedb here in case it helps others with extremely slow extraction: https://github.com/libyal/libesedb/issues/8

    Once I got the ntds.dit.export from libesedb 20140406, I used ntdsxtract dsusers.py (about another 2 hours to complete) as documented on this page for final success on obtaining all domain hashes: http://www.ntdsxtract.com/. The several randomly chosen password hash samples I tested yielded successful pass-the-hash authentication, so I know I got good hashes with this method.

    Btw, I also tried the secretsdump.py method suggested in an earlier post against my local ntds.dit file using the latest version of Impacket 0.9.13, however that generated lots of utf-16 errors during extraction and the hashes that it did recover were invalid and would not successfully auth using pass-the-hash tests.

    So, final conclusion, for a very large AD where smart_hashdump doesn’t work for whatever reason on the DC or you have concerns it could crash the system as harmj0y brought up, this is what worked for me:

    a. The ntdsutil/esentutl method to extract/repair the ntds.dit and registry hives on the target DC. Make sure you have enough disk space on the DC’s local drive for the full size of the ntds.dit plus another 5-10% needed for the esentutl repair process. (1-2 hours to complete in my case)

    b. Download the ntds.dit and registry hives on to your linux box (don’t forget to clean up the copies on the DC afterwards) and then use libesedb version 20140406 to parse/create the export on your local system. (26-27 hours to complete in my case with a 30GB+ ntds.dit on a decent x64 Linux workstation)

    c. Lastly use ntdsxtract v1.3 / dsusers.py for the final parse and LM and NT hash extraction. (2 hours to complete in my case). This is the command line I used:
    python dsusers.py ./ntds.dit.export/datatable.4 ./ntds.dit.export/link_table.6 –syshive ./registry-hive/SYSTEM –passwordhashes –passwordhistory –pwdformat john –lmoutfile ./lmhashes.txt –ntoutfile ./nthashes.txt

    Where datatable.4 & link_table.6 were created by the libesedb extract and SYSTEM is the registry hive you downloaded from the ntdsutil copy on the target DC.

    Whole thing start to finish took about 30 hours but worth it.
    Hope this helps anyone else out there having fun with ntds.dit extractions.

  3. Just a quick correction, looks like the path portion of the commands were removed by filters, so here are the full ntdsutil and esentutl commands again with the corrections:

    ntdsutil.exe “activate instance ntds” “ifm” “Create Full PATH-TO-SAVE” quit quit
    esentutl.exe /p /o “PATH-TO-SAVE\Active Directory\ntds.dit”

    Where PATH-TO-SAVE is the path where you save the ntds.dit and registry hives to.

    Ex.
    ntdsutil.exe “activate instance ntds” “ifm” “Create Full C:\Temp” quit quit
    esentutl.exe /p /o “C:\Temp\Active Directory\ntds.dit”

    This method is also documented in the recent domain_hashdump.rb Metasploit post module, however since it could take hours to run, I chose to run it manually.

  4. Great article!

    “Once I get to a DC, I try not to use Meterpreter’s smart_hashdump if I can help it. There is a particular defensive product that has given us heartburn when trying to use this module to dump hashes from a live domain controller.”

    Mind sharing which product? :)

Leave a Reply to Wintermute4316 Cancel Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.