It’s read-only Friday so I decided to perform a offline audit of our Active Directory passwords.
I found this great tool: https://gitlab.com/chelmzy/five-minute-password-audit which in turn is a fork of this tool: https://github.com/DGG-IT/Match-ADHashes
What I’m going to write here is mostly a repeat of these two Gitrepos with a few tweaks and corrections.
To perform this procedure you will need to be able to login to a Domain Controller. You’re also going to want a secure location to perform all of this work so the dumped list of usernames and hashes doesn’t escape your control.
The secure location should be a workstation or server running the same or a newer version of Windows than your Domain Controller. For example if you’re running AD 2012R2 you can’t complete this on a 2008R2 box. You’re secure workstation or server will need to be running PowerShell 5.0 or newer.
Step 1 – Export NTDS.dit and the SYSTEM hive
- Login to a domain controller
- Open a Command Prompt window
- Type “ntdsutil”
- Click ‘Yes’ if the UAC prompts you
- Run the following commands:
activate instance ntds ifm # Replace <DOMAINNAME> with your domains name create full c:\temp\<DOMAINNAME>-audit # Wait for command to complete quit quit
- Transfer “C:\Temp\<DOMAINNAME>-audit” to the secure location you’ll work on it. I do not recommend performing the rest of these steps on your Domain Controllers
Step 2 – Download the latest Have I Been Pwned Offline NTLM password list
- Go to https://haveibeenpwned.com/Passwords
- Scroll to the bottom and download the “ordered by prevalence” NTLM link
- Once downloaded, transfer the password list to your secure location in the audit directory and extract it
Step 3 – Covert the hashes in the NTDS.dit file to Hashcat formatting
- On your secure workstation/server launch PowerShell as an administrator (right click, run as administrator on the PowerShell shortcut)
- Install the DSInternals tools by running
Install-Module -Name DSInternals -Force
- Go into the audit directory
cd c:\temp\<DOMAINNAME>-audit
- Convert the hashes
$key = Get-BootKey -SystemHivePath .\registry\SYSTEM # Change <DOMAINNAME> to your domains name Get-ADDBAccount -All -DBPath '.\Active Directory\ntds.dit' -BootKey $key | Format-Custom -View HashcatNT | Out-File <DOMAINNAME>-hashes.txt -Encoding ASCII
Step 4 – Compare your hashes to HIBP
The code in the Git Repos I linked at the beginning of the article are written as functions. For myself I just wanted a script I could execute with the appropriate parameters instead of futzing around with importing the function.
I also tweaked the original script for formatting (I like a bit more white space personally), added CSV headers, removed the spaces between commas, had the script append it’s execution time to the end of the CSV file and allowed for relative filenames as parameters instead of requiring absolute paths.
Here is my version of the script:
<# This is a slightly altered version of https://gitlab.com/chelmzy/five-minute-password-audit/blob/master/Match-ADHashes.ps1 which is a slightly alter version of https://github.com/DGG-IT/Match-ADHashes/ for no nonsense output. All credit to them. .NAME Match-ADHashes .SYNOPSIS Matches AD NTLM Hashes against other list of hashes .DESCRIPTION Builds a hashmap of AD NTLM hashes/usernames and iterates through a second list of hashes checking for the existence of each entry in the AD NTLM hashmap -Outputs results as object including username, hash, and frequency in database -Frequency is included in output to provide additional context on the password. A high frequency (> 5) may indicate password is commonly used and not necessarily linked to specific user's password re-use. .PARAMETER ADNTHashes File Path to 'Hashcat' formatted .txt file (username:hash) .PARAMETER HashDictionary File Path to 'Troy Hunt Pwned Passwords' formatted .txt file (HASH:frequencycount) .PARAMETER Verbose Provide run-time of function in Verbose output .EXAMPLE $results = Match-ADHashes -ADNTHashes C:\temp\adnthashes.txt -HashDictionary -C:\temp\Hashlist.txt .OUTPUTS Array of HashTables with properties "User", "Frequency", "Hash" User Frequency Hash ---- --------- ---- {TestUser2, TestUser3} 20129 H1H1H1H1H1H1H1H1H1H1H1H1H1H1H1H1 {TestUser1} 1 H2H2H2H2H2H2H2H2H2H2H2H2H2H2H2H2 .NOTES If you are seeing results for User truncated as {user1, user2, user3...} consider modifying the Preference variable $FormatEnumerationLimit (set to -1 for unlimited) =INSPIRATION / SOURCES / RELATED WORK -DSInternal Project https://www.dsinternals.com -Checkpot Project https://github.com/ryhanson/checkpot/ =FUTURE WORK -Performance Testing, optimization -Other Languages (golang?) .LINK https://github.com/DGG-IT/Match-ADHashes/ #> param( [Parameter(Mandatory = $true)] [System.IO.FileInfo] $ADNTHashes, [Parameter(Mandatory = $true)] [System.IO.FileInfo] $HashDictionary ) process { $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() # Set the current location so .NET will be nice and accept relative paths [Environment]::CurrentDirectory = Get-Location # Declare and fill new hashtable with ADNThashes. Converts to upper case to $htADNTHashes = @{} Import-Csv -Delimiter ":" -Path $ADNTHashes -Header "User","Hash" | % {$htADNTHashes[$_.Hash.toUpper()] += @($_.User)} # Create Filestream reader $fsHashDictionary = New-Object IO.Filestream $HashDictionary,'Open','Read','Read' $frHashDictionary = New-Object System.IO.StreamReader($fsHashDictionary) # Output CSV headers Write-Output "Username,Frequency,Hash" #Iterate through HashDictionary checking each hash against ADNTHashes while ($null -ne ($lineHashDictionary = $frHashDictionary.ReadLine())) { if($htADNTHashes.ContainsKey($lineHashDictionary.Split(":")[0].ToUpper())) { $user = $htADNTHashes[$lineHashDictionary.Split(":")[0].ToUpper()] $frequency = $lineHashDictionary.Split(":")[1] $hash = $linehashDictionary.Split(":")[0].ToUpper() Write-Output "$user,$frequency,$hash" } } $stopwatch.Stop() Write-Output "Function Match-ADHashes completed in $($stopwatch.Elapsed.TotalSeconds) Seconds" } end { }
To execute it, copy/paste it into notepad and save it as ‘myAudit.ps1’ or what ever file name you’d like.
Now perform your audit:
# Replace <DOMAINNAME> with your domain name .\myAudit.ps1 -ADNTHashes <DOMAINNAME>-hashes.txt -HashDictionary <HIBP TEXT FILE> | Out-File <DOMAINNAME>-PasswordAudit.csv # Example .\myAudit.ps1 -ADNTHashes myDomain-hashes.txt -HashDictionary pwned-passwords-ntlm-ordered-by-count-v5.txt | Out-File myDomain-PasswordAudit.csv
The final result will be a CSV file you can dig through.
Step 6 – Clean it all up
The output may or may not surprise you but what ever the outcome, when you’re done you want to get rid of the <DOMAINNAME>-hashes.txt and the NTDIR.dis file as soon as possible. If someone snags a copy of that you’ll likely get in some serious trouble.
Head on over to SysInternals and grab SDelete
.\sdelete.exe -p 7 -r -s <DIRECTORY OR FILE>