# hashstat: Turning a Raw NTDS Dump Into a Report

By Leon Johnson, 2026-08-13. Web version: https://sholuv.net/blog/posts/hashstat-ntds-analysis/

So you've compromised an Active Directory environment, owned the domain controller, and dumped the `NTDS.dit`. Look how cool you are. Now what?

Because what you're staring at is a wall of hashes:

```text
corp.acme.com\john.doe:1104:aad3b435b51404eeaad3b435b51404ee:5f4dcc3b5aa765d61d8327deb882cf99:::
```

That line tells you john.doe has an account and no LM hash stored. It doesn't tell you if the account is enabled, if he's a domain admin, or if his password is any good. The one thing you can pull out yourself is reuse: NTLM isn't salted, we don't use salts here people, so the same password makes the same NT hash. Grep it across the dump and you've found everyone who shares it.

Everything else has to be enriched. So I built [hashstat](https://github.com/sho-luv/hashstat) to do all of it in one pass.

![hashstat demo](https://raw.githubusercontent.com/sho-luv/hashstat/master/hashstat-demo.gif)

## What it does

Point it at a pwdump or secretsdump file. It's a single Python file, zero dependencies, so copy it and run it:

```bash
./hashstat.py ntds.dit -a
```

Feed it your John or hashcat results and it folds in the cracked plaintext. Feed it a BloodHound export and it tags every account enabled/disabled, admin, kerberoastable, and password age. Disabled accounts drop off your reset list by default (they're a delete-list, not a reset-list) but nothing gets filtered at collection time, so you keep the whole picture.

The census, run against the bundled synthetic sample:

```text
══ OVERVIEW
  User accounts                       5,004
    analyzed (enabled)                4,381
    excluded (disabled)                 623
  using LM hash                     2,248   (51%)
  Reusing a password                    583
  Cracked (weak)                      4,381
  AFFECTED (reuse or weak)            4,381  (100%)
```

I lead the report with the privileged reuse. Run it with `-s` so it's safe to screenshot:

```text
══ SECURITY STORY  privileged & tier-crossing reuse

  ▸ Admin reusing their OWN password on a standard account
      · a_john.doe  ↔  john.doe   [pw: <pw-23·6ch>]

  ▸ Service accounts sharing a password
      · svc_reporting  shares with 1 other acct(s)   [pw: <pw-28·11ch>]
```

Every list (`-u` reset hand-off, `-D` delete list, cracked, LM) exports to CSV, and `-s` swaps every password and hash for a stable alias so a screenshot proves *who* shares a secret without leaking it.

## Try it

The repo ships a fully synthetic ACME dataset, so you can try every feature offline without touching real credentials:

```bash
./hashstat.py sample/ntds.dit.sample -a \
  --potfile sample/ntds.dit.sample.pot \
  --bloodhound sample
```

Grab it from [GitHub](https://github.com/sho-luv/hashstat). One file, standard library, no dependencies.

## by Leon Johnson
