HEVD Use-After-Free scenario¶
HackSys Extreme Vulnerable Driver UAF object'ini free eder ama global pointer'ı asla null'lamaz, dolayısıyla freed NonPagedPool chunk'ını aynı boyutta sahte bir object ile reclaim etmek driver'ın kontrollü bir function pointer'ı çağırmasına izin verir.
Mechanism¶
HEVD, NonPagedPool üzerinde ExAllocatePoolWithTag ile küçük bir object allocate eder, adresini bir global'de (g_UseAfterFreeObject) saklar ve içine bir Callback function pointer'ı kaydeder. "use" IOCTL'i bu global'i dereference eder ve gömülü Callback non-NULL ise onu çağırır.
Note
Bug, free üzerinde eksik bir invariant'tır: FreeUaFObjectNonPagedPool ExFreePoolWithTag'i çağırır ama g_UseAfterFreeObject = NULL yapmaz. Global artık freed bir chunk'a giden bir dangling pointer'dır. Pool allocator'ın o tam chunk'ı aynı size class'ın bir sonraki allocation isteğine geri vermesine izin vardır. Attacker "use" IOCTL'i çalışmadan önce kontrollü bir allocation'ı o freed adrese düşürebilirse, driver Callback field'ını attacker kontrollü byte'lardan okur ve onu call eder. Buradaki UAF aslında bir free-list reuse race'idir: chunk'ı kim reclaim ederse object'in içeriğine sahip olur, buna kernel'in güveneceği function pointer da dahildir.
Reclaim object'i UAF object'i ile aynı boyuttadır, dolayısıyla aynı pool bucket'ına / Low-Fragmentation-Heap bucket'ına düşer ve allocator'ın onu az önce free edilen chunk'tan karşılayabilmesini garantiler.
Walkthrough¶
Dört IOCTL (HEVD'nin Windows 10/11 x64 build'i; kod'lar build başına biraz değişir — bunlar klasik x86 değerleridir):
| İşlem | IOCTL |
|---|---|
AllocateUaFObjectNonPagedPool |
0x222013 |
UseUaFObjectNonPagedPool |
0x222017 |
FreeUaFObjectNonPagedPool |
0x22201B |
AllocateFakeObjectNonPagedPool |
0x22201F |
Driver UAF object'i için 0x58 byte ister; allocator bunu 0x10 granularity'sine göre 0x60 byte'lık bir pool block'una yuvarlar (rounded-up allocation size, gömülü pool header ayrıca tutulur). Reclaim için kullanılan IoCompletionReserve object'i de aynı 0x60 block'a düşer. Callback function pointer'ı object'in başında durur, dolayısıyla UseUaFObjectNonPagedPool'un dereference ettiği ilk şeydir.
Exploit dizisi:
- Groom. NonPagedPool'u çok sayıda
0x60byte'lık object ile spray'le, sonra bir atlayıp birini free et ki target bucket attacker kontrollü hole'larla dolsun. Klasik spray araçları:NtAllocateReserveObject(IoCompletionReserve, "IoCo") veya named-pipe attribute / pipe-queue entry'leri —0x60'a boyutlandırabileceğin herhangi bir object. - Allocate ile UAF object'ini allocate et:
DeviceIoControl(hHevd, 0x222013, ...). Driver "Use After Free Object Allocated" yazar. - Free ile onu free et:
DeviceIoControl(hHevd, 0x22201B, ...).g_UseAfterFreeObjectartık dangling'dir. - Reclaim. Hemen
AllocateFakeObjectNonPagedPool(0x22201F) ile aynı boyutta sahte object'leri spray'le; ilk 8 byte'ıCallbackdeğerin olan bir buffer kopyala (onu token-steal çalıştıran bir usermode shellcode page'ine, ya da SMEP altında bir ROP pivot'a işaret ettir). - Trigger.
DeviceIoControl(hHevd, 0x222017, ...). Driver artık kontrol edilenCallback'i okur ve onu kernel context'inde çağırır.
Expected driver debug output (DbgView)
Warning
SMEP/SMAP ve kCFG altında Callback doğrudan usermode shellcode'a işaret edemez. Kontrollü call'u bir stack pivot + ROP'a dönüştür, ya da data-only bir token-stealing payload kullan. Modern build'lerde global bazı variant'larda null'lanmış olabilir — dangling pointer'a güvenmeden önce tam HEVD revizyonunu doğrula.
Detection¶
EDR / Special Pool'lu Driver Verifier, "use" path'inde freed bir chunk'a erişimi (use-after-free) yakalar. Davranışsal imza, \\.\HackSysExtremeVulnerableDriver'a karşı sıkı bir allocate → free → spray → ioctl döngüsü ve bir driver'dan usermode veya yeni spray'lenmiş bir adrese giden indirect bir call'dur.
Mitigation¶
Driver Verifier (Special Pool, force pending I/O) freed chunk'ı page-protected yapar, böylece UAF sessizce reclaim etmek yerine fault verir. Gerçek düzeltme, free üzerinde pointer'ı NULL'lamaktır (g_UseAfterFreeObject = NULL). SMEP/SMAP, kCFG ve HVCI kontrollü call'u code execution'a dönüştürmenin maliyetini artırır.