
Extracts the current user's NetNTLMv2 hash via HTTP authentication proxying, avoiding direct SSPI calls; v2 delegates auth to the BITS service to break process attribution.
NTLM hash extraction through HTTP-layer authentication proxying, zero SSPI calls from the attacker process.
HashSiphon extracts the current user's NetNTLMv2 hash by manipulating HTTP authentication flows instead of calling SSPI APIs directly. It ships two variants: v1 routes NTLM auth through .NET's HTTP stack within the same process, and v2 delegates authentication entirely to the BITS service (svchost.exe) in a different PID, breaking process-level attribution altogether.
Every known NTLM hash self-extraction tool Internal Monologue, manual SSPI scripts, and their derivatives calls AcquireCredentialsHandle → InitializeSecurityContext → AcceptSecurityContext from the attacker's own process. EDRs hook these SSPI functions and flag the call chain.
HashSiphon takes a fundamentally different path:
| Aspect | Internal Monologue | HashSiphon v1 | HashSiphon v2 |
|---|---|---|---|
| SSPI calls from attacker PID | 4+ direct calls | 0 direct (WinHTTP calls internally) | 0 in any process we own |
| Auth process | Attacker PID | Attacker PID (via HTTP stack) | svchost.exe (BITS service) |
| Security API imports | SSPI DLL imports required | None in our code | None in our code |
| Detection surface | SSPI hooks, API call patterns | HTTP loopback traffic | BITS job + loopback traffic |
| Process attribution | Attacker PID | Attacker PID | Broken, different PID entirely |
Both variants share the same core: a minimal TCP server on 127.0.0.1 that speaks just enough HTTP to perform an NTLM challenge-response exchange, with a controlled 8-byte challenge so the captured hash is crackable offline.
HashSiphon.ps1)┌──────────────────────────┐
│ PowerShell (PID X) │
│ │
│ ┌────────────────────┐ │ ┌──────────────────────┐
│ │ TCP Server (C#) │◄─┼──────────┤ HttpWebRequest + │
│ │ Loopback :random │ │ HTTP │ DefaultCredentials │
│ │ │──┼──────────► │
│ │ 1. Send 401+NTLM │ │ NTLM │ WinHTTP auto-auths │
│ │ 2. Send Type 2 │ │ Type │ using current user's │
│ │ 3. Capture Type 3 │ │ 1/2/3 │ credentials │
│ │ 4. Extract hash │ │ │ │
│ └────────────────────┘ │ └──────────────────────┘
└──────────────────────────┘
Add-Type, binds to 127.0.0.1:0 (OS-assigned port)HttpWebRequest + CredentialCache.DefaultCredentialsHTTP 401 with WWW-Authenticate: NTLM to trigger negotiationTrade-off: WinHTTP internally calls SSPI within the same PID, no direct imports, but the call stack still traces back to us.
HashSiphonV2.ps1)┌─────────────────────┐ ┌───────────────────────────┐
│ PowerShell (PID X) │ │ svchost.exe (PID Y) │
│ │ │ BITS Service │
│ ┌───────────────┐ │ HTTP │ │
│ │ TCP Server │◄─┼─────────┤ BITS downloads from our │
│ │ (Background │ │ NTLM │ server, auto-authenticates│
│ │ Runspace) │──┼─────────► using job owner's creds │
│ └───────────────┘ │ Type │ │
│ │ 1/2/3 │ SSPI calls happen HERE, │
│ Start-BitsTransfer─┼────────►│ not in PID X │
│ (Trigger only) │ COM │ │
└─────────────────────┘ └───────────────────────────┘
│
└── Our process: TcpListener + Start-BitsTransfer
Zero SSPI. Zero security API imports.
Start-BitsTransfer creates a download job pointing at http://127.0.0.1:<port>/hashsiphon.binsvchost.exe, a completely different PID) connects to our serverHTTP 200 with a body so BITS considers the transfer successfulBreakthrough: Our process never calls SSPI, not directly, not through WinHTTP, not at all. The entire NTLM computation happens in svchost.exe. EDR SSPI hooks see the call stack in the BITS service process, not ours.
powershell -ExecutionPolicy Bypass -File HashSiphon.ps1
powershell -ExecutionPolicy Bypass -File HashSiphonV2.ps1
[*] Compiling HashSiphon v2 server...
[+] Server compiled
HashSiphon v2.0 - BITS Service Proxy Authentication
Auth by svchost.exe (BITS), not our process
[1] NTLM HTTP server ready on 127.0.0.1:52847
[2] Controlled challenge: 1122334455667788
[3] Triggering BITS transfer to our server...
[4] BITS transfer initiated
[+] User: ivan
[+] Domain: DESKTOP-ABCDEF
[+] NT response: 280 bytes (NTLMv2)
+----------------------------------------------------------+
| NetNTLMv2 HASH - Extracted via BITS service proxy! |
+----------------------------------------------------------+
| hashcat -m 5600 | john --format=netntlmv2 |
+----------------------------------------------------------+
ivan::DESKTOP-ABCDEF:1122334455667788:<NTProofStr>:<ClientBlob>
+----------------------------------------------------------+
| ATTRIBUTION ANALYSIS |
+----------------------------------------------------------+
| Our PID: 844 (PowerShell) |
| Auth by: BITS service (svchost.exe, PID 5500) |
| SSPI calls: Zero from PID 844 |
| Our APIs: TcpListener + Start-BitsTransfer only |
+----------------------------------------------------------+
If BITS fails (service disabled, module unavailable), v2 automatically falls back to launching a child powershell.exe process with Invoke-WebRequest -UseDefaultCredentials. This still achieves PID separation, though the child process is more visible than BITS.