Step 263. Active Directory 3 — Pass-the-Hash and BloodHound
Level 3 — Real-World CTF & Advanced Offensive Skills | Difficulty ★★★★☆ | Estimated time: 4 hours
Prerequisites: Step 261’s domain structure and Step 262’s ticket cracking. What an NTLM hash is gets taught today.
- What you need: An AD lab on THM/HTB. This environment has neither an AD domain nor BloodHound, so all attack scenes and BloodHound screens are presented as output/Screen examples — learning the principles and how to read graphs is today’s practice.
- ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
In Step 262 you cracked tickets to get passwords. But in the field, what lands in your hands first is often not a password but a hash — from the memory or SAM file of a machine you’ve taken. If you can’t crack it to plaintext, is it useless? Here enters one painful design fact of Windows NTLM authentication — the hash is the password. And in the second half of today: BloodHound, the map tool that computes "where to go next" with the credentials you’ve obtained. Hash reuse (Pass-the-Hash) and path computation (graph analysis) — these two are the working engine of AD intrusion.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Explain why the NTLM challenge-response structure creates "hash = password"
- Know the procedure and limits of authenticating with just a hash via Pass-the-Hash
- Know what data BloodHound collects (nodes, edges) and what graph paths mean
- Interpret "Shortest Path to Domain Admins" query results as attack steps
- Organize the defensive perspective for PtH and BloodHound respectively
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | Kali (Impacket) + BloodHound (neo4j) — all output/Screen examples based on labs |
| Today’s tools | psexec.py (Impacket), SharpHound / bloodhound-python, BloodHound GUI |
| Concepts needed | NTLM hashes, challenge-response authentication, graphs (nodes/edges), AdminTo·MemberOf·HasSession |
| Today’s deliverable | A one-paragraph PtH principle write-up + BloodHound path-interpretation notes |
2-1. The Bone of NTLM Authentication — Challenge-Response
Windows’ older (but still active) authentication scheme, NTLM, works like this:
- Client: "I’d like to connect as alice"
- Server: throws a random number (the challenge) — "prove it with this"
- Client: encrypts the challenge with the NTLM hash of its own password (the response) and returns it
- Server: computes the same thing with the alice hash it knows and compares — match means pass
Stop here and look — what the server checks is not the plaintext password but the response made from the hash. Whether the client derived the hash from plaintext or picked the hash up somewhere, to the server the same hash = the same person. It’s a design where plaintext never crosses the wire — but in exchange, the hash itself became a complete key. This is why Pass-the-Hash (PtH) — the attack of "passing the hash as-is" — works.
2-2. How PtH Unfolds — Where Hashes Come From
The standard plot of an intrusion scenario: enter machine A through a web vulnerability → obtain SYSTEM via escalation (Step 260) → dump hashes from memory or SAM → among them, find a hash that also works on other machines (local administrator, or a domain account that has logged in) → authenticate to machine B with the hash as-is → dump again on B → ….
This is what lateral movement looks like — not digging deep into one machine, but harvesting credentials and spreading sideways, sideways. The destination was set in Step 261 — Domain Admins. The problem is that "from the account in my hand, the path to the throne is not visible," and solving that problem is the second half’s BloodHound.
2-3. BloodHound — A Tool That Bakes AD Into a Graph
BloodHound scrapes all of AD’s relationships, turns them into a graph (dots and lines), and computes "the shortest path from here to Domain Admins." The graph’s two parts:
- Nodes (dots): users, groups, computers — e.g.,
ALICE@CORP.LOCAL,DOMAIN ADMINS@CORP.LOCAL,DB01.CORP.LOCAL - Edges (lines): relationships —
MemberOf(member of this group),AdminTo(administrator of that computer),HasSession(a login session of this account is alive on that computer), etc.
One path, read aloud in words, goes like this — "ALICE (node) is a member of the Helpdesk group (MemberOf), Helpdesk is administrator of DB01 (AdminTo), and on DB01 a session of admin BOB is alive (HasSession)." Translated into attack procedure: log in as alice → enter DB01 with admin rights → harvest bob’s credentials from memory → move to the next node as bob. One edge = one attack step. The map (Step 261) has become a computable form.
2-4. Separating Collection from Analysis — SharpHound and neo4j
BloodHound runs as two chunks. The collector that scrapes data in the target domain — SharpHound on Windows, bloodhound-python on Kali — and the analyzer that bakes that data into a graph (the BloodHound GUI, backed by a graph DB called neo4j). Bring the zip the collector made into the GUI on your machine, and pre-built queries — the throne among them being "Shortest Path to Domain Admins" — draw the path graphs for you.
One practical tip: installing neo4j is a famously annoying wall. These days, bringing the whole thing up with a single docker compose file is convenient — remember it if you get stuck in the lab.
2-5. The Defensive View — How to Block the Two Techniques
- PtH defense: ① If local administrator passwords differ per machine, hash reuse breaks — LAPS (Local Administrator Password Solution, automated per-machine local admin passwords) is the answer. ② Blocking the hash dump itself — Credential Guard, least privilege. ③ Moving to an environment that reduces NTLM itself and uses only Kerberos (then PtH doesn’t work at all — though Step 262’s attacks remain instead).
- BloodHound defense: Perfect blocking is hard — most enumeration is normal LDAP queries. The effective defense is keeping paths short — revoke unnecessary local admin rights, let admin accounts log in only from dedicated machines (leave no sessions behind), clean up excessive group nesting. Even if the attacker can draw the graph, make it so no line connects to the throne.
3. Follow Along
This environment has neither an AD domain nor BloodHound, so today is training to read output/Screen examples and interpret them by hand. Commands are in real form, usable as-is in labs.
3-1. Hand-Drawing Challenge-Response — Why the Hash Is the Key
Draw 2-1’s four steps on paper. Beside the fourth step, write this question — "what does the server compare: plaintext, or a response made from the hash?" The answer is the response made from the hash. In this picture’s conclusion box, engrave today’s sentence: "To an NTLM server, the hash is the identity. Plaintext is unnecessary."
You might want to go further and compute a hash yourself — but the NTLM hash is MD4-based, and in modern Python/OpenSSL environments md4 is disabled, so hashlib.new('md4', ...) fails with unsupported hash type md4 (confirmed by measurement 2026-09-09). That’s how old the algorithm is, and lab tools (Impacket) handle the computation for you.
3-2. PtH Authentication — Output Example
Say the hash harvested from machine A is aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0 (LM:NT format; the aad3b435... in the LM slot is the famous fixed value meaning "empty"). Impacket’s psexec.py accepts a hash instead of plaintext:
psexec.py corp.local/administrator@10.10.10.20 -hashes :31d6cfe0d16ae931b73c59d7e0c089c0
Output example:
[*] Requesting shares on 10.10.10.20.....
[*] Found writable share ADMIN$
[*] Uploading file pMSxKqIa.exe
[*] Opening SVCManager on 10.10.10.20.....
[*] Starting service ...
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.20348.XXXX]
C:\Windows\system32> whoami
nt authority\system
How to read it: The plaintext password is nowhere — a single NT hash after -hashes opened an nt authority\system shell on 10.10.10.20. Internally it works by uploading and running a temporary service on the remote machine (which is why Starting service appears in the output) — connecting to Step 260’s knowledge that "services are Windows’ auto-execution channel." This is the scene where 2-1’s sentence, the hash is the password, is proven on screen.
3-3. When PtH Fails — Output Example
The same command can fail like this (output example):
[*] Requesting shares on 10.10.10.30.....
[-] SMB SessionError: STATUS_LOGON_FAILURE(The attempted logon is invalid.)
How to read it: Candidates for STATUS_LOGON_FAILURE — ① the hash is wrong for that machine (same account, different password — normal in a LAPS-deployed environment), ② an environment where NTLM is disabled and only Kerberos is accepted, ③ account lockout/restriction. Failure is information too — it’s a measurement of the environment’s defensive level: "hash reuse is severed here." In that case, crack the hash (Step 123) for the plaintext, or find another edge (next section’s graph).
3-4. BloodHound Collection — Output Example
Run the collector with one domain account (from Kali, output example):
bloodhound-python -u alice -p 'Password123' -d corp.local -dc dc01.corp.local -c All
INFO: Found AD domain: corp.local
INFO: Connecting to LDAP server: dc01.corp.local
INFO: Found 38 users
INFO: Found 5 groups
INFO: Found 6 computers
...
INFO: Done in 00:12
INFO: Compressing output into 20260909..._bloodhound.zip
How to read it: "38 users, 5 groups, 6 computers" — the domain’s skeleton fit into one zip. This is Step 261’s "drawing the domain map" done by a machine in 12 seconds. On the Windows side, SharpHound.exe -c All does the same job (though it’s a flagship tool Defender catches — turn it off in labs; in real work, assume detection).
3-5. BloodHound Analysis — Screen Example
Feed the zip into the BloodHound GUI and run the pre-built query "Shortest Path to Domain Admins." Screen example (the graph transcribed to text):
ALICE@CORP.LOCAL
│ MemberOf
▼
HELPDESK@CORP.LOCAL
│ AdminTo
▼
DB01.CORP.LOCAL
│ HasSession ← a session of BOB@CORP.LOCAL is alive
▼
BOB@CORP.LOCAL
│ MemberOf
▼
DOMAIN ADMINS@CORP.LOCAL
How to read it — this is today’s summit. Translate the four edges into attack procedure:
ALICE → HELPDESK (MemberOf): alice is a member of the Helpdesk group — already mineHELPDESK → DB01 (AdminTo): this group is local admin of DB01 — alice can access DB01 as administratorDB01 → BOB (HasSession): bob’s login session is alive on DB01 — with admin rights, bob’s credentials (hash) can be harvested from memoryBOB → DOMAIN ADMINS (MemberOf): bob is a member of Domain Admins — PtH with the harvested hash reaches the throne
Four lines of graph are a four-step attack plan. On the real screen this graph is drawn among hundreds of nodes, and when several paths exist, it shows the shortest (cheapest) ones first.
3-6. Writing Path-Interpretation Notes
Organize 3-5’s graph in your notes like this — edge / the attack technique at that spot / the tool needed:
| Edge | Attack step | Tool |
|---|---|---|
| MemberOf (ALICE→HELPDESK) | Confirm held credentials | — |
| AdminTo (HELPDESK→DB01) | Remote admin access | psexec.py etc. |
| HasSession (DB01→BOB) | Harvest credentials from memory | dump tools (lab) |
| MemberOf (BOB→DA) | Authenticate to the throne via PtH | psexec.py -hashes |
If you can complete this table yourself, today’s goal is met — you’ve learned to read a graph not as "a picture" but as "an operations plan."
4. Missions & Exercises
Mission — PtH Principle Paragraph + BloodHound Path Interpretation
- Draw 3-1’s challenge-response picture and write one paragraph on "why authentication works with a hash and no plaintext"
- (If you have a lab) Run
psexec.py -hasheswith a harvested hash and save thewhoamiresult as evidence — if it fails, write the failure interpretation from 3-3 - (In the lab) Feed a zip collected with SharpHound or
bloodhound-pythoninto the GUI and run "Shortest Path to Domain Admins" - Translate every edge of the resulting path into "attack step + tool" in the 3-6 table format
- From the defender’s view, pick the easiest edge to cut in that path and write your reasoning — save as
ad-path-analysis.md
Exercises
Q1. Explain why the fact that an NTLM server verifies a hash-made response rather than plaintext leads to "stolen hash = stolen account."
Q2. State what a HasSession edge in a BloodHound graph means to an attacker, and how a defender reduces these edges.
Q3. Explain the principle by which LAPS (different local admin passwords per machine) breaks PtH lateral movement.
Q4. Explain why PtH doesn’t work in an environment where NTLM is fully disabled and only Kerberos is used — and what risks remain nonetheless (hint: Step 262).
5. Model Answers & Completion Criteria
Mission Model Answer
Example principle paragraph: "In NTLM authentication, what the server compares is the client’s ‘challenge encrypted with the hash’ response against the response it computes itself. Plaintext travels nowhere in the process. Therefore whoever knows the hash can produce a correct response without the plaintext, and to the server the hash is the ID card — a stolen hash becomes a key as-is, no cracking needed."
How to verify path interpretation: ① Was every edge of the path translated into a table row without omissions? ② Does each row show "what is obtained to move to the next"? ③ Is PtH evidence (the whoami result) or failure interpretation (candidate analysis of STATUS_LOGON_FAILURE) attached?
Model defense analysis: "The easiest edge to cut in this path is HasSession — if high-privilege accounts like bob never leave sessions on ordinary servers like DB01 (a dedicated admin workstation policy), the harvestable credential disappears and the path breaks." — The key understanding is that cutting one edge nullifies the whole path.
Exercise Answers
A1. Since the evidence material of authentication is the hash rather than the plaintext, the hash is everything needed to pass verification. The attacker can encrypt the challenge with the hash to produce a correct response, and the server has no way to tell it’s stolen goods. Theft that needs no cracking (plaintext recovery) — this is what makes PtH frightening, and why protecting hash stores (SAM, memory) is protecting accounts.
A2. To an attacker, HasSession means "this account’s credentials are alive on that computer right now" — a marker showing where harvestable goods sit, needing only admin rights to collect. The defender’s methods: let high-privilege accounts log in only from dedicated admin devices and never on ordinary servers (tiered administration); reduce unnecessary session residue — with no session, this edge simply doesn’t exist.
A3. The fuel of PtH lateral movement is "the same hash working across many machines" — if every machine uses the same local admin password, one machine’s dump is the key to all. LAPS has AD manage a different random password per machine, so a hash obtained on machine A is valid only on A and yields STATUS_LOGON_FAILURE on B. The link of hash reuse breaks, and lateral movement stops at machine granularity.
A4. PtH relies on the property that NTLM’s challenge-response "makes responses from hashes," so without NTLM itself, the target that property exploits disappears. But Kerberos has its own credential attacks — service ticket cracking (Kerberoasting) and pre-auth-disabled accounts (AS-REP Roasting) work without NTLM (Step 262). Swap protocols and the danger of the form "take a ciphertext and crack it offline" disappears only when the design changes.
Completion Criteria Checklist
- [ ] Can draw the four steps of NTLM challenge-response
- [ ] Can state in one sentence why "hash = password" holds
- [ ] Can read the structure and output of the
psexec.py -hashescommand - [ ] Know the three candidate causes of
STATUS_LOGON_FAILURE - [ ] Can explain BloodHound’s nodes/edges and the flagship edges (MemberOf·AdminTo·HasSession)
- [ ] Can translate a path graph into an attack-step table
- [ ] Mission: completed the principle paragraph +
ad-path-analysis.md
6. Common Pitfalls & Fixes
Wall 1. psexec.py throws STATUS_LOGON_FAILURE
Symptom (output example): [-] SMB SessionError: STATUS_LOGON_FAILURE(...)
Candidate causes: the three from 3-3 — hash mismatch (per-machine different passwords = LAPS), NTLM-disabled environment, account lockout.
Fix: First confirm which account the hash belongs to — a local admin hash belongs to that machine, while a domain account hash can be tried across the whole domain. If a domain account still fails, suspect NTLM being disabled and switch to the Kerberos path (Step 262). Build the habit of recording failure not as "stuck" but as "a measurement of this environment’s defenses."
Wall 2. Entering the hash in the wrong format
Symptom: Authentication fails with the value you attached to -hashes.
Cause: The format is an LM:NT pair, and common slips are dropping the famous empty LM value (aad3b435b51404eeaad3b435b51404ee) or dropping the leading colon when using only NT.
Fix: With only the NT hash, the correct form is -hashes :NThash — the leading colon marks "LM is empty." Copying the dump tool’s output wholesale is safest.
Wall 3. BloodHound won’t come up — the neo4j installation swamp
Symptom: The GUI starts but DB connection errors, or you burn all your time installing neo4j.
Cause: BloodHound needs the neo4j graph DB behind it, and version compatibility is famously finicky.
Fix: Don’t fight manual installation; use a docker compose setup — recent BloodHound (CE) comes up whole, DB included, with a single official compose file. Spending lab time on tool installation is putting the cart before the horse — raise the container and get to the main event (reading graphs).
Wall 4. Collection done but the graph is empty
Symptom: You fed the zip in but no paths appear.
Candidate causes: ① Insufficient collection options — session info (HasSession) requires separate collection. ② There really is no path (a well-managed domain).
Fix: Check that you collected everything with -c All. If it’s still empty, that may be the conclusion — "no line connects from my current account to DA," evidence of a well-defended environment and a line in your report. Then you aim for another starting point (harvest another account).
Wall 5. The graph is too complex to read
Symptom: Hundreds of nodes with edges tangled like a maze.
Cause: Normal — a real domain’s graph really looks like that.
Fix: Don’t try to take in the whole thing. BloodHound’s pre-built queries — "Shortest Path to Domain Admins," "Find Principals with DCSync Rights," etc. — act as filters. Run one query → translate just the path that comes out into a 3-6 table → next query. Today’s skill is not understanding the whole maze but pulling one thread out of the maze and reading it.
7. Summary
Today’s Concepts
| Concept | One-line description |
|---|---|
| Pass-the-Hash | NTLM authentication with the NT hash alone, no plaintext — the hash is the identity |
| Challenge-response | What the server verifies is a hash-made response — the basis of PtH |
| Lateral movement | Harvesting credentials and spreading to neighboring machines |
| BloodHound | AD relationships as a graph — a "shortest path to DA" calculator |
| Nodes / edges | Dots (accounts, groups, machines) and lines (MemberOf·AdminTo·HasSession) |
| LAPS | Different local admin passwords per machine — blocks hash reuse |
| HasSession hygiene | No high-privilege session residue — the key edge of path-cutting |
Today’s Commands (All Output Examples)
| Command | What it does |
|---|---|
psexec.py domain/user@IP -hashes :NThash |
Remote auth & shell with hash alone |
bloodhound-python -u ... -d domain -c All |
Collect domain relationship data |
SharpHound.exe -c All |
Windows-side collector (Defender detection target) |
| "Shortest Path to Domain Admins" | The throne-path query in the BloodHound GUI |
The Instinct That Matters More Than Commands
Step 261’s map, 262’s cracked tickets, today’s hashes and graphs — the AD trilogy is really one story. "Credentials get reused in changed forms, and relationships are a computable map." Plaintext, hash, or ticket — a credential in hand opens the next door, and BloodHound shows the way those doors connect. The defender cuts this serial with the same eyes — make passwords uncrackable (gMSA), break hash reuse (LAPS), leave no sessions (HasSession hygiene), keep paths short. To the extent attackers read the map, defenders must read the map too — the staring contest over the same graph is AD security’s true form.
Once every box is checked, Step 263 is complete. Click the checkbox in the sidebar to save your progress.