Skip to content

Finding Pwned Passwords in Active Directory

Finding pwned passwords in Active Directory doesn’t need to be taxing. Choosing the right approach can save time and mitigate instability risk. This complete guide walks through each approach, pros and cons and some quicker alternatives.

Weak and pwned passwords accounted for 73% of breaches in the last year, as reported by Verizon and Rapid7. Do you know how many of your users are using a blacklisted password?

If you test user passwords, you’ll know Microsoft has never made it easy. There are always tricks to export password hashes but each method has its pros and cons.


The New NIST Password Guidelines make sensible new recommendations. The current climate of data breaches is at the heart of one of its major changes. That is: check a user password against a corpus of breached data.

A password audit is a very effective way of demonstrating this area of weakness. Two of the most prevalent attacks today: Password Spraying and Credential Stuffing.


This is a two-step process. Dump the hashes from a DC first, and then compare the hashes to a list of breached passwords/hashes. Various ways to grab the hashes exist, each carrying some risk as it’s an unsupported process. Techniques for obtaining the hashes from a Windows Domain Controller boil down to:

  • Local Security Authority Subsystem Service (LSASS) injection
  • Shadow Copy replication with Microsoft Vssadmin
  • (Ab)Using the Domain Replication Service

Local Security Authority Subsystem Service (LSASS) Injection

Dumping the LSASS (Local Security Authority Subsystem) process space is the oldest method. This is the historical way of extracting domain hashes within a Windows eco-system. Several tools and techniques exist to do that, one of the most common and reliable is Mimikatz.

Start mimikatz.exe and type the following commands:

log mimikatz-output.txt
lsadump::lsa /inject /patch

The first command takes care of granting the privileges required. The second sets a log file for the output. The final command instructs the tool on which technique to use (LSASS Injection).

Once this is complete, the log file created should look like this:

mimikatz output

But it needs to be in a pwdump format and to look like this:


Note: this is not the full pwdump format but it’s all that’s needed here.

A few lines of awk (after stripping out the header/footer of the log file) will suffice:

# cat mimi.awk
BEGIN { RS = "" ; FS = "\n" ; ORS =""; OFS=""}
{ sub(/User : /,""); print $2 ","}
{ sub(/NTLM : /,""); print $4}
{ print "\n"}

Problems with this technique:

  • Risks crashing / blue screening the server
  • Very likely to trigger an AV
  • Slow and cumbersome (takes a long time to parse the memory space)
  • It’s not secure (hashes will need to be scrubbed).

Using The Windows Tools

A safer way is to rely on the Windows built-in Vssadmin (Volume Shadow Copy) utility. Vssadmin can take a copy of the c:\Windows\NTDS\NTDS.dit file (this file is locked as it’s used by LSASS).

This method is less disruptive, much less likely to get caught by AV and unlocks the password history too. It can take up a lot of space, as the NTDS.dit can grow pretty large. It also might increase the risk of detection and network disruption as a result.

To create a shadow copy and copy the required files (NTDS.dit, SYSTEM, SAM), the commands are as follows:

Create Shadow Copy

vssadmin create shadow /for=C:
vssadmin shadowcopy

Copy NTDS.dit, SYSTEM and SAM

copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\system32\ntds\ntds.dit c:\temp
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\system32\config\system c:\temp
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\system32\config\sam c:\temp
copy system sam


vssadmin List shadows
vssadmin Delete Shadows /shadow={Shadow Copy ID}
delete shadow copy

The DSInternals Powershell Module will convert it into a suitable format for cracking:

$key = Get-BootKey -SystemHiveFilePath 'c:\temp\system'
Get-ADDBAccount -All -DBPath 'c:\temp\ntds.dit' -Bootkey $key | Format-Custom -View HashcatNT | Out-File shadow-hashes.txt -Encoding ASCII

The above will process a copy of the NTDS.dit file, extract user and hash information, format it in a hashcat-compatible output and write it to a file.

shadow hashes

(Ab)Using the Domain Replication Service

The safest method of obtaining domain hashes is to (ab)use the ‘Domain Replication Service’. This works by temporarily spawning up a new Domain Controller on the network and syncing up the credential storage to it.

The steps to perform this are as follows:

  1. Install the DS-Internals Powershell Module
  2. Set the credentials
  3. Export the Hashes from AD
  4. Run the script.

The DSInternals package needs to be installed, as follows:

Install-Module -Name DSInternals
dsinternals install

Enter credentials by running the following powershell command:

$credentials = Get-Credential

The following will dump those pesky hashes:

Get-ADReplAccount -all -NamingContext “DC=Example,DC=org” -Server DC1 -Credential $mycredentials | Format-Custom -View HashcatNT | Out-File c:\temp\hashes.txt -Encoding ASCII

Note: Ensure you change the domain and DC according to your environment (here Example, org, and DC1).

The above will produce a hashcat-compatible ASCII plain-text file to compare against the HIBP hashes.

This is how hashes.txt will look like:

Identifying Vulnerable User Accounts

So which users on the network are vulnerable? Let’s explore a couple of ways of doing this.

Using Wordlists

A good wordlist of compromised passwords is needed. There are various lists of cracked passwords over at, such as:

John the Ripper and Hashcat are amongst the most respected crackers out there. Usage for these is as follows:

Using JtR (John the Ripper)

john --format=nt hashes.hashcat

Using Hashcat

hashcat -m 1000 -a 0 --username hashes.hashcat

On a very modest system, it takes less than a couple of minutes to run through the dictionary file which results in the output below:

Viewing the results

$ john --show --format=nt hashes.hashcat|tail

323 password hashes cracked, 329 left


$ hashcat --username --show -m 1000 hashes.hashcat |tail


To perform this check offline, download a copy of the Have I Been Pwned database, in NTLM format (ordered by hash). It’s not a good idea to perform the password check online.


Extracting it (with 7zip) can take a while:


The final step is to compare the HIBP database containing the NTLM hashes (sorted by hash) in c:\temp\ with the extracted hashes in the same folder.

This script will make it easy:

powershell -Executionpolicy bypass
Import-Module .\Match-ADHashes

The comparison of the hashes is done using the Match-ADHashes function.

$list = Match-ADHashes -ADNTHashes C:\temp\hashes.txt -HashDictionary C:\temp\hibp.txt

The above command will populate the $list list variable with the results (Note: this might take a while). The results can be exported into a more useful format and write it to disk:

$list | select Hash,Frequency,@{Name=’user’;Expression={[string]::join(“;”, ($_.user))}} | Export-Csv -Path c:\temp\pwned-users-report.csv -Delimiter ‘;’ -NoTypeInformation

Note: Frequency is the number of times that password(hash) has been seen collectively within the Have I Been Pwned leaked database.

The above will pipe the contents of $list into a CSV file with the matching hash, a count and the list of pwned users.

pwned users notepad

Password Analysis


Pipal is a useful utility written by Robin Wood to perform an analysis of user passwords. You have to have access to the plain-text of the password in order to gather any useful information.

Usage of the tool is pretty simple, no external library is needed, just Ruby installed.

Running Pipal on the results obtained yields the following output:

$ ./pipal.rb found.txt
Generating stats, hit CTRL-C to finish early and dump stats on words already processed.
Please wait...
Processing:    100% |oooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:00

Basic Results

Total entries = 346
Total unique entries = 285

Top 10 passwords
Butterfly29 = 3 (0.87%)
Password2 = 3 (0.87%)
Reind33r = 3 (0.87%)
Tanzania3 = 3 (0.87%)
Password284 = 3 (0.87%)
Tilling7 = 3 (0.87%)
Christian7 = 3 (0.87%)
Babybrain3 = 3 (0.87%)
1111111111 = 2 (0.58%)
Molly154 = 2 (0.58%)

Top 10 base words
password = 28 (8.09%)
welcome = 5 (1.45%)
london = 5 (1.45%)
victoria = 4 (1.16%)
tilling = 3 (0.87%)
tanzania = 3 (0.87%)
christmas = 3 (0.87%)
jessica = 3 (0.87%)
april = 3 (0.87%)
liverpool = 3 (0.87%)

Password length (length ordered)
8 = 155 (44.8%)
9 = 85 (24.57%)
10 = 57 (16.47%)
11 = 34 (9.83%)
12 = 14 (4.05%)
14 = 1 (0.29%)

Password length (count ordered)
8 = 155 (44.8%)
9 = 85 (24.57%)
10 = 57 (16.47%)
11 = 34 (9.83%)
12 = 14 (4.05%)
14 = 1 (0.29%)

<--output stripped for brevity-->

Pipal can be used to get a good insight into what common passwords are being used on the Active Directory Domain being tested. This knowledge can be used to create relevant exclusion wordlists to prevent users from setting common, easy-to-guess passwords.

A Different/Better Approach

It’s easy to check how many Windows domain users are using compromised passwords. It’s also rather convoluted and error-prone as well as very time-consuming.

What if there were an automated way of checking this that:

  • Gives instant results (a few minutes vs hours/days)
  • Is comprehensive, repeatable and deterministic
  • Is secure (doesn’t leave a trace of the domain hashes anywhere)
  • Doesn’t require software installation
  • Is user-friendly (not just for ubergeeks)
  • Doesn’t need Domain Admin privileges.

The efficiency of this process can be significantly improved. Comparison times can be optimized down to milliseconds.

Running Pwncheck

Pwncheck is the most optimal way of getting a list of pwned users. No installation is needed, nor Domain Admin privileges.

The 3 privileges that pwncheck needs can be granted like so:

#Just substitute the pwncheck account below with the account you want to assign replication privileges with

$Account = "pwncheck"

$RootDSE = [ADSI]"LDAP://RootDSE"
$DefaultNamingContext = $RootDse.defaultNamingContext

$cmd = "dsacls '$DefaultNamingContext' /G '`"$Account`":CA;`"Replicating Directory Changes`";'"
Invoke-Expression $cmd
$cmd = "dsacls '$DefaultNamingContext' /G '`"$Account`":CA;`"Replicating Directory Changes All`";'"
Invoke-Expression $cmd
$cmd = "dsacls '$DefaultNamingContext' /G '`"$Account`":CA;`"Replicating Directory Changes In Filtered Set`";'"
Invoke-Expression $cmd

Running pwncheck is a three-step process:

  1. Start the pwncheck executable
  2. Wait for it to download/load the DB
  3. Enter your credentials and click Go
pwncheck gui

After a few seconds, the pwncheck GUI will:

  • Write the report to disk
  • Securely scrub the process memory space and close its main window
  • Open up the report folder to reveal the report data.

The README.html file containing the password audit results can then be viewed for the full report:

pwncheck report

The pwncheck report dashboard

pwncheck report 1
pwncheck report 2
pwncheck report 3

If you like this new approach, or if you have any comments, we’d love to hear from you!

You can email me at .

You can see it in action here: pwncheck demo