
HTTP認証プロキシ経由で現在のユーザーのNetNTLMv2ハッシュを抽出し、直接のSSPI呼び出しを回避します。v2は認証をBITSサービスに委任してプロセスの帰属を断ち切ります。
HTTP層の認証プロキシを介したNTLMハッシュ抽出、攻撃者プロセスからのSSPI呼び出しはゼロ。
HashSiphonは、SSPI APIを直接呼び出す代わりにHTTP認証フローを操作することで、現在のユーザーのNetNTLMv2ハッシュを抽出します。2つのバリアントを提供しています: v1 は同一プロセス内で.NETのHTTPスタックを介してNTLM認証をルーティングし、v2 は認証を完全に別のPIDのBITSサービス(svchost.exe)に委譲し、プロセスレベルの帰属を完全に断ち切ります。
既知のNTLMハッシュ自己抽出ツールはすべて Internal Monologue、手動のSSPIスクリプト、およびその派生ツールは、攻撃者自身のプロセスから AcquireCredentialsHandle → InitializeSecurityContext → AcceptSecurityContext を呼び出します。EDRはこれらのSSPI関数をフックし、呼び出しチェーンを検知します。
HashSiphonは根本的に異なる経路を取ります:
| 観点 | Internal Monologue | HashSiphon v1 | HashSiphon v2 |
|---|---|---|---|
| 攻撃者PIDからのSSPI呼び出し | 4回以上の直接呼び出し | 直接0回 (WinHTTPが内部的に呼び出す) | 当方が所有するどのプロセスでも0回 |
| 認証プロセス | 攻撃者PID | 攻撃者PID (HTTPスタック経由) | svchost.exe (BITSサービス) |
| セキュリティAPIインポート | SSPI DLLインポートが必要 | 当方のコードにはなし | 当方のコードにはなし |
| 検知対象 | SSPIフック、API呼び出しパターン | HTTPループバックトラフィック | BITSジョブ + ループバックトラフィック |
| プロセス帰属 | 攻撃者PID | 攻撃者PID | 破綻、完全に別のPID |
両バリアントは同じコアを共有しています: 127.0.0.1 上の最小限のTCPサーバーで、NTLMチャレンジレスポンス交換を実行するのに十分なHTTPを話し、キャプチャしたハッシュをオフラインでクラック可能にするために制御された8バイトのチャレンジを使用します。
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 を介してC# TCPサーバーをコンパイルし、127.0.0.1:0 (OS割り当てポート) にバインドHttpWebRequest + CredentialCache.DefaultCredentials で接続HTTP 401 と WWW-Authenticate: NTLM を返してネゴシエーションをトリガートレードオフ: WinHTTPは内部的に同一PID内でSSPIを呼び出します。直接的なインポートはありませんが、呼び出しスタックは依然として当方に遡ります。
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 が http://127.0.0.1:<port>/hashsiphon.bin を指すダウンロードジョブを作成svchost.exe、完全に別のPID) が当方のサーバーに接続HTTP 200 を返し、BITSが転送を成功と見なす画期的な点: 当方のプロセスはSSPIを一切呼び出しません。直接的にも、WinHTTP経由でも、まったく呼び出しません。NTLM計算の全体が svchost.exe 内で行われます。EDRのSSPIフックは、当方ではなくBITSサービスプロセス内の呼び出しスタックを目にします。
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 |
+----------------------------------------------------------+
BITSが失敗した場合 (サービス無効、モジュール利用不可)、v2は自動的に子 powershell.exe プロセスを Invoke-WebRequest -UseDefaultCredentials で起動するフォールバックを行います。これでもPID分離は達成されますが、子プロセスはBITSよりも可視性が高くなります。