
Extrait le hash NetNTLMv2 de l'utilisateur actuel via un proxy d'authentification HTTP, évitant les appels SSPI directs ; la v2 délègue l'authentification au service BITS pour rompre l'attribution du processus.
Extraction de hash NTLM via un proxy d'authentification au niveau de la couche HTTP, zéro appel SSPI depuis le processus de l'attaquant.
HashSiphon extrait le hash NetNTLMv2 de l'utilisateur courant en manipulant les flux d'authentification HTTP au lieu d'appeler directement les API SSPI. Il est livré en deux variantes : v1 achemine l'authentification NTLM via la pile HTTP de .NET au sein du même processus, et v2 délègue entièrement l'authentification au service BITS (svchost.exe) dans un PID différent, brisant ainsi toute attribution au niveau du processus.
Tous les outils connus d'auto-extraction de hash NTLM Internal Monologue, les scripts SSPI manuels et leurs dérivés appellent AcquireCredentialsHandle → InitializeSecurityContext → AcceptSecurityContext depuis le processus de l'attaquant lui-même. Les EDR hookent ces fonctions SSPI et signalent la chaîne d'appels.
HashSiphon emprunte une voie fondamentalement différente :
| Aspect | Internal Monologue | HashSiphon v1 | HashSiphon v2 |
|---|
| Appels SSPI depuis le PID de l'attaquant | 4+ appels directs | 0 direct (WinHTTP appelle en interne) | 0 dans tout processus que nous possédons |
| Processus d'authentification | PID de l'attaquant | PID de l'attaquant (via la pile HTTP) | svchost.exe (service BITS) |
| Imports d'API de sécurité | Imports de DLL SSPI requis | Aucun dans notre code | Aucun dans notre code |
| Surface de détection | Hooks SSPI, motifs d'appels API | Trafic HTTP en loopback | Tâche BITS + trafic en loopback |
| Attribution du processus | PID de l'attaquant | PID de l'attaquant | Brisée, PID entièrement différent |
Les deux variantes partagent le même cœur : un serveur TCP minimal sur 127.0.0.1 qui parle juste assez HTTP pour effectuer un échange challenge-réponse NTLM, avec un challenge contrôlé de 8 octets afin que le hash capturé soit cassable hors ligne.
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, se lie à 127.0.0.1:0 (port attribué par l'OS)HttpWebRequest + CredentialCache.DefaultCredentialsHTTP 401 avec WWW-Authenticate: NTLM pour déclencher la négociationCompromis : WinHTTP appelle SSPI en interne au sein du même PID, aucun import direct, mais la pile d'appels remonte toujours jusqu'à nous.
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 crée une tâche de téléchargement pointant vers http://127.0.0.1:<port>/hashsiphon.binsvchost.exe, un PID complètement différent) se connecte à notre serveurHTTP 200 avec un corps afin que BITS considère le transfert comme réussiPercée : Notre processus n'appelle jamais SSPI, ni directement, ni via WinHTTP, jamais. L'intégralité du calcul NTLM se produit dans svchost.exe. Les hooks SSPI de l'EDR voient la pile d'appels dans le processus du service BITS, pas dans le nôtre.
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 |
+----------------------------------------------------------+
Si BITS échoue (service désactivé, module indisponible), v2 bascule automatiquement vers le lancement d'un processus enfant powershell.exe avec Invoke-WebRequest -UseDefaultCredentials. Cela permet tout de même d'obtenir la séparation des PID, bien que le processus enfant soit plus visible que BITS.