
HTTP 인증 프록시를 통해 현재 사용자의 NetNTLMv2 해시를 추출하며, 직접적인 SSPI 호출을 피합니다. v2는 인증을 BITS 서비스에 위임하여 프로세스 귀속을 끊습니다.
HTTP 계층 인증 프록시를 통한 NTLM 해시 추출, 공격자 프로세스에서 SSPI 호출 제로.
HashSiphon은 SSPI API를 직접 호출하는 대신 HTTP 인증 흐름을 조작하여 현재 사용자의 NetNTLMv2 해시를 추출합니다. 두 가지 변형을 제공합니다: 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에서 NTLM 챌린지-응답 교환을 수행할 수 있을 만큼만 HTTP를 구현하는 최소 TCP 서버이며, 캡처된 해시를 오프라인에서 크래킹할 수 있도록 제어된 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는 자동으로 Invoke-WebRequest -UseDefaultCredentials로 자식 powershell.exe 프로세스를 실행하는 것으로 폴백합니다. 이는 여전히 PID 분리를 달성하지만, 자식 프로세스는 BITS보다 더 눈에 띕니다.