Skip to content

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:

  1. Groom. NonPagedPool'u çok sayıda 0x60 byte'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.
  2. Allocate ile UAF object'ini allocate et: DeviceIoControl(hHevd, 0x222013, ...). Driver "Use After Free Object Allocated" yazar.
  3. Free ile onu free et: DeviceIoControl(hHevd, 0x22201B, ...). g_UseAfterFreeObject artık dangling'dir.
  4. Reclaim. Hemen AllocateFakeObjectNonPagedPool (0x22201F) ile aynı boyutta sahte object'leri spray'le; ilk 8 byte'ı Callback değ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).
  5. Trigger. DeviceIoControl(hHevd, 0x222017, ...). Driver artık kontrol edilen Callback'i okur ve onu kernel context'inde çağırır.
Expected driver debug output (DbgView)
[+] Use After Free Object Allocated: 0xFFFFA00C12345000
[+] Pool Tag: Hack
[+] Pool Type: NonPagedPool
[+] Freeing UaF Object
[+] Fake Object Allocated: 0xFFFFA00C12345000   <-- same address reclaimed
[+] Using UaF Object
[+] Callback: 0x00000001401000   <-- attacker-controlled pointer

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.

References