Dumping ntds.dit files using PowerShell

October 20, 2015 | Michael Grafnetter

Although there exist several tools for dumping password hashes from the Active Directory database files, including the open-source NTDSXtract from Csaba Bárta whose great research started it all, they have these limitations:

  • They do not support the built-in indices, so searching for a single object is slow when dealing with large databases.
  • Most of the tools are either Linux-only or running them on Windows is not simple enough.
  • Almost none of these tools can modify the database. And if they do, they do not support transaction logs and are quite cumbersome.

Therefore, I have decided to create my own set of PowerShell cmdlets that wouldn’t have these shortcomings. In the process, I have unintentionally created my own framework that is built on top of Microsoft’s ManagedEsent library and hides the complexity of the underlying database. I am planning to release it at GitHub later this year.

One of the cmdlets I have created is Get-ADDBAccount, which can be used to extract password hashes, Kerberos keys and even reversibly encrypted passwords from ntds.dit files. Here is an example of its usage:

The output is identical to what the Get-ADReplAccount cmdlet would return:

I have also created several Views that generate output for the most popular password crackers, including Hashcat, John the Ripper and Ophcrack:

But with the Golden Ticket or Pass-the-Hash functionality of mimikatz, an attacker could seize control of the entire Active Directory forest even without cracking those password hashes.

As a countermeasure, it is crucial for companies to secure physical access to domain controllers, their backups and their VHD/VHDX/VMDK images in case of virtualized DCs. Turning on BitLocker is not a bad idea either. I really look forward to the new security features planned for Windows Server 2016, including Shielded VMs and Virtual TPMs.


Tags: , ,

28 comments on “Dumping ntds.dit files using PowerShell

  1. thierry says:

    Hello
    Congratulations for this great work I am currently testing the safety of my Active Directory with your powershell module and meets the following error message:

    PS C:\Users\admin> Get-BootKey -SystemHivePath ‘D:\tmp\ifm\registry\SYSTEM’
    040b02c9bd79ae68a95ccde275e12868
    PS C:\Users\admin> $key = Get-BootKey -SystemHivePath ‘D:\tmp\ifm\registry\SYSTEM’
    PS C:\Users\admin> Get-ADDBAccount -all -DBPath ‘D:\tmp\ifm\ActiveDirectory\ntds.dit’ -BootKey $key
    Get-ADDBAccount : Cannot marshal: Encountered unmappable character.
    Au caractère Ligne:1 : 1
    + Get-ADDBAccount -all -DBPath ‘D:\tmp\ifm\ActiveDirectory\ntds.dit’ -BootKey $key
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Get-ADDBAccount], ArgumentException
    + FullyQualifiedErrorId : DBContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand I fail to see where does the problem, thank you for your help

  2. Michael Grafnetter says:

    This problem only occurs on Windows 7/2008. I know about it and it is caused by a library I am using (C Microsoft). I have reported the bug and I am awaiting a new release. Unfortunately, I currently cannot do much more about it.

  3. lee says:

    hi. thanks for working on this. i was testing in my lab (windows 2012 active directory) and am getting errors. how did you create the files: ‘C:\IFM\registry\SYSTEM’ and ‘C:\IFM\Active Directory\ntds.dit’? i used vssadmin create shadow /for=c:, and then copied SYSTEM and ntds.dit to a temp directory, but when i try to create the $key variable from your example i get: Get-BootKey : The configuration registry database is corrupt.

  4. Michael Grafnetter says:

    Hi lee, I used ntdsutil IFM. It is the simplest way and generates a correct SYSTEM hive. Or use reg save I am using Windows-built-in mechanism of mounting the hive rather than my own, so it cannot be in a dirty state.

  5. Nick says:

    Get a weird error.

    ntds.dit and SYSTEM is exported from a win2008R2 server using ntdsutil. Running the powershell module from a win2012R2.

    Unblocked the module zip file before before installing.

    PS C:\> $key = Get-BootKey -SystemHivePath “C:\SYSTEM”
    PS C:\>
    PS C:\> $key
    2bc5ae2c28662f04b23a33008c743be8
    PS C:\>
    PS C:\> Get-ADDBAccount -All -DBPath “C:\ntds.dit” -BootKey $key
    Get-ADDBAccount : Parameter is not a hexadecimal string.
    At line:1 char:1
    + Get-ADDBAccount -All -DBPath “C:\ntds.dit” -BootKey $key
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Get-ADDBAccount], ArgumentException
    + FullyQualifiedErrorId : DBContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand

  6. Michael Grafnetter says:

    OK, that is weird, I will look into it. Could you please send me stack trace of that exception?

  7. Is there a way to access AD password hashes over the network?
    This appears to read the hashes using Esent, which means you have to be on the same machine as a domain controller.

  8. Another question. I am doing this on my domain controller.

    $bk = Get-BootKey -Online
    Get-ADDBAccount -samAccountName kevin -DBPath ‘C:\windows\ntds\ntds.dit’ -BootKey $bk and I am getting an error that the file is locked, which is because the ad service is running and using it. Do you have to stop my ad service in order to use this powershell command? Is there a way to access the db while it is in use?

  9. Michael Grafnetter says:

    Look at this article of mine: Retrieving Active Directory Passwords Remotely

  10. Michael Grafnetter says:

    See my previous answer. And the Esent library is present on all Windows systems. So it is possible to copy a ntds.dit file from Windows Server 2012 R2 and open it on Windows 8.1. If a different Windows kernel shall be used, the database must be defragmented first using esentutil.

  11. KevD says:

    This is awesome stuff!
    One note I wanted to make on the PowerShell example listed to dump to a hash file for use with John the Ripper… By default, out-file in PowerShell will create the text file in Unicode format, which will not be recognized by JtR. You will need to append ‘-encoding ascii’ to export to a file that will work.
    Other than that, this works fabulously!
    Thanks!

  12. Martin Handl says:

    Very sweet tool! A fast way to get to the samaccountnames:

    (Get-ADDBAccount -All -DBPath .\ntds.dit -key (Get-Content -path .\syskey.txt)).samaccountname | Out-File .\users.txt

  13. Drew R says:

    Getting trapped with the error below. I can run the first two lines of the script and see the output fly across the screen, but it always ends with this error. When I add the output function, it just fails with this same error. Any ideas?
    PS C:\WINDOWS\system32> Get-ADDBAccount -All -DBPath ‘C:\ntds.dit’ -BootKey $nkey |
    Format-Custom -View Ophcrack |
    Out-File ‘c:\hashes.txt’
    Get-ADDBAccount : Value cannot be null.
    Parameter name: value
    At line:1 char:1
    + Get-ADDBAccount -All -DBPath ‘C:\ntds.dit’ -BootKey $nkey |
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-ADDBAccount], ArgumentNullException
    + FullyQualifiedErrorId : System.ArgumentNullException,DSInternals.PowerShell.Commands.GetADDBAccountCommand

  14. Michael Grafnetter says:

    Thank you, KevD, for your remark. I have updated the example in the article accordingly.

  15. Michael Grafnetter says:

    Hi Drew, could you please send me the stack trace of this exception? $Error[0].Exception.StackTrace

  16. John says:

    Hi there. This seams to be working great however it is showing no password. ClearText field is empty. Am I doing something wrong?

    Thank you

  17. Michael Grafnetter says:

    Hi John, the ClearText field only contains a value if the option “Store password using reversible encryption” is enabled on the specific account or globally.

  18. Martin Handl says:

    As it seems is the encryption used for the AD database somewhat different to 2012R2 DCs. When I try to get – for example – the krgtgt account the following result occurs:

    #retrieving the bootkey
    $bootkey = (Get-ChildItem -Path C:\temp -Include *pfx -Recurse).Basename.split(“_”)[-1].Replace(“-“,””)

    #retrieving the krbtgt account from ntds.dit
    Get-ADDBAccount -SamAccountName krbtgt -DBPath C:\NTDS\ntds.dit -BootKey $bootkey

    Get-ADDBAccount : Invalid PEK signature.
    At line:1 char:1
    + Get-ADDBAccount -SamAccountName krbtgt -DBPath C:\NTDS\ntds.dit -BootKey $bootke …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-ADDBAccount], Exception
    + FullyQualifiedErrorId : System.Exception,DSInternals.PowerShell.Commands.GetADDBAccountCommand I am just at the moment trying the same thing at the same domain with a 2012R2 DC. I will keep you informed

  19. Martin Handl says:

    Disregard everything! I mistook the Backupkey with the syskey

  20. Nikola says:

    Hello,Michael!

    Dump from w2008 r2 SYSTEM + ntds.dit

    Open win7 x64 install power shell v5 and run You script

    $key = Get-BootKey -SystemHivePath ‘c:\system’;
    $key ok

    Get-ADDBAccount -All -DBPath ‘c:\ntds.dit’ -BootKey $key

    result
    ====================================================
    Get-ADDBAccount : The database is not in a clean state. Try to recover it first by running the ‘esentutl /r edb /d’ com
    mand.
    At line:1 char:1
    + Get-ADDBAccount -All -DBPath ‘c:\ntds.dit’ -BootKey $key |Format
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo: OpenError: (:) [Get-ADDBAccount], InvalidDatabaseStateException
    + FullyQualifiedErrorId : BContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand
    ====================================================== After run
    esentutl /r c:\ntds.dit /d

    Operation terminated with error -1003 (JET_errInvalidParameter, Invalid API parameter) after 0.0 seconds.

  21. Michael Grafnetter says:

    Nikola, there might be a problem with access rights. Please, try putting the ntds.dit file into a folder.

  22. Ubermedina says:

    Getting the error below when following the above tutorial.

    Get-ADDBAccount : Unicode normalization failed
    At line:1 char:1

    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Get-ADDBAccount], EsentUnicodeTranslationFailException
    + FullyQualifiedErrorId : DBContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand

  23. Ryan says:

    Hello Michael, I have exactly the same error as Nikola up there. I exported ntds.dit and SYSTEM from a shadow copy on a W2008R2 and tried to use your tools on a W7x64 machine with a fresh Powershellv5. I made sure to work within a folder where I have full control.
    Any further hint very much appreciated!
    Thanks++
    Ryan

    PS C:\Users\Ryan\Documents\AD\NTDS> $key = Get-BootKey -SystemHivePath .\SYSTEM

    PS C:\Users\Ryan\Documents\AD\NTDS> Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey $key
    Get-ADDBAccount : The database is not in a clean state. Try to recover it first by running the ‘esentutl /r edb /d’ command.
    At line:1 char:1
    + Get-ADDBAccount -All -DBPath .\ntds.dit -BootKey $key
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (:) [Get-ADDBAccount], InvalidDatabaseStateException
    + FullyQualifiedErrorId : DBContextError,DSInternals.PowerShell.Commands.GetADDBAccountCommand

    PS C:\Users\Ryan\Documents\AD\NTDS> esentutl /r ntds.dit /d

    Extensible Storage Engine Utilities for Microsoft(R) Windows(R)
    Version 6.1
    Copyright (C) Microsoft Corporation. All Rights Reserved.

    Initiating RECOVERY mode…
    Logfile base name: ntds.dit
    Log files:
    System files:
    Database Directory:

    Operation terminated with error -1003 (JET_errInvalidParameter, Invalid API parameter) after 0.0 seconds.

  24. Michael Grafnetter says:

    Hi Ryan, you are apparently running “esentutl /r ntds.dit /d” instead of “esentutl /r edb /d”…

  25. Michael Grafnetter says:

    Hi Ubermedina, never seen this exception. Could you please send me the OS version of the DC and of the computer on which you are using the DSInternals module? What regional settings are you using on both of these systems?

  26. Don C says:

    Hey Michael,

    Great info! Thanks for doing this. I keep running into the following error after this step:
    Get-ADDBAccount -all -DBPath ‘C:\temp\ntds.dit’ -BootKey $key

    Get-ADDBAccount : Cannot bind parameter ‘BootKey’. Cannot convert value “55f59f858ee5a3a6f5a941d95401d272” to type “System.Byte”. Error: “Input string was not in a correct format.”
    At line:1 char:64
    + Get-ADDBAccount -all -DBPath ‘C:\temp\ntds.dit’ -BootKey $key
    + ~~~~
    + CategoryInfo : InvalidArgument: (:) [Get-ADDBAccount], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,DSInternals.PowerShell.Commands.GetADDBAccountCo
    mmand

    Have you seen this before? I am running it on Win8.1, powershell ver 4, ntds.dit and system is from 2012R2.

  27. Simon says:

    Hi Michael. I have a problem I hope you can help with. I can successfully get my boot key with Get-Bootkey and I can see it is correct. However, when I run:

    Get-ADDBAccount -All -DBPath ‘C:\temp\ntds.dit’ -BootKey $key I get the following error:

    Get-ADDBAccount : Cannot bind parameter ‘BootKey’. Cannot convert value “f15d29209a8b3c2de0e8eb9ba02c9aeb” to type
    “System.Byte”. Error: “Input string was not in a correct format.”
    At line:1 char:62
    + Get-ADDBAccount -All -DBPath ‘C:\temp\ntds.dit’ -BootKey $key
    + ~~~~
    + CategoryInfo : InvalidArgument: (:) [Get-ADDBAccount], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,DSInternals.PowerShell.Commands.GetADDBAccountCommand

    This is on Win 10 using .Net 4.6.2 and DSInternals 2.21. Is this an error you have come across before?

    Thanks in advance for any help you can offer.

    Simon

  28. Michael Grafnetter says:

    Don, the bug will be resolved in the next release. Please use GutHub for bug reports, that platform is much more suitable for this purpose.

Leave a Reply

Your email address will not be published.