Skip to content

NtQuerySystemInformation kernel-address disclosure

NtQuerySystemInformation'ın bir dizi information class'ı (SystemModuleInformation, SystemHandleInformation, SystemBigPoolInformation, SystemExtendedHandleInformation) yıllarca medium-IL caller'lara ham kernel pointer'ları döndürdü ve tek bir syscall ile kASLR'ı çökertti; ExIsRestrictedCaller bunu önce below-Medium caller'lar için, sonra da 24H2'de SeDebugPrivilege yoksa herkes için kapattı.

Mechanism

NtQuerySystemInformation (dahili ExpQuerySystemInformation) bir "SYSTEM_INFORMATION_CLASS" alır ve o class'a karşılık gelen kernel bookkeeping yapısını user buffer'a kopyalar. Sorun, bu yapıların birçoğunun tasarımı gereği ham kernel virtual address taşımasıdır: SystemModuleInformation her yüklü modülün DllBase'ini, SystemHandleInformation / SystemExtendedHandleInformation her object'in kernel Object pointer'ını, SystemBigPoolInformation ise her big-pool allocation'ının VirtualAddress'ini içerir. Bir syscall, ntoskrnl ve driver base'lerini plus canlı pool/object adreslerini user mode'a yankılıyordu.

Note

Buradaki invariant bir memory-safety bug'ı değil, bir policy eksikliğidir: kernel, diagnostic amaçlı tutulan pointer'ları trust boundary'yi düşünmeden geri veriyordu. kASLR'ın tüm değeri "attacker base'i bilmiyor" varsayımına dayanır; bu class'lar o varsayımı sıfır effort'la yıkar — heap groom, race ya da OOB read gerekmez. Çünkü leak'in kaynağı bir zafiyet değil, dokümante edilmiş bir API'nin niyetlenmiş çıktısıdır; savunma da bu yüzden bir patch değil, caller'a göre alanları zero'lama politikasıdır.

Windows 8.1 bu boşluğu ExIsRestrictedCaller ile daraltmaya başladı: fonksiyon, integrity level'ı Medium'un altında olan (Low / Untrusted / AppContainer — yani sandbox'lı IE, Chrome, Reader) caller'lar için TRUE döner ve çağıran kod (örn. ExpQueryModuleInformation) address alanlarını 0 yazar. Ama Medium IL ve üzeri "unrestricted" sayıldığından, sandbox'sız bir local process için leak yıllarca canlı kaldı.

Walkthrough

Public PoC'lerin (sam-b/windows_kernel_address_leaks) izlediği high-level mantık:

// 1. Boyutu öğrenmek için sıfır buffer'la çağır (STATUS_INFO_LENGTH_MISMATCH).
ULONG len = 0;
NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &len);

// 2. Buffer ayır ve tekrar sorgula.
auto* mods = (RTL_PROCESS_MODULES*)malloc(len);
NtQuerySystemInformation(SystemModuleInformation, mods, len, &len);

// 3. mods->Modules[i].ImageBase => ntoskrnl.exe / driver kernel base'i.
//    (Medium IL'de dolu; below-Medium'da 0'lanmış gelir.)

Kavramsal adımlar:

  1. Class seçimi. Image base için SystemModuleInformation; belirli bir object'in (ör. hedef process token'ı) kernel adresi için SystemHandleInformation / SystemExtendedHandleInformation; pool grooming hedefi için SystemBigPoolInformation.
  2. İki-geçişli sorgu. Önce length'i öğren, sonra buffer'ı doldur — klasik NtQuerySystemInformation idiom'u.
  3. Pointer çıkarımı. İlgili struct field'ından (ImageBase / Object / VirtualAddress) kernel address'i oku.
  4. kASLR çözümü. Leak edilen base bir sonraki write/execute primitive'inin hedef offset'lerini hesaplamak için kullanılır — leak burada exploit chain'in "recon" evresidir, silah değil.
Neden bu kadar cazipti

Tek başına bir memory-corruption bug'ı çoğu zaman sadece bir R/W primitive verir ama nereye yazacağını bilmen için bir base gerekir. Bu class'lar o base'i syscall maliyetiyle sağladığı için, medium-IL LPE exploit'leri (browser sandbox'tan sonra veya doğrudan local) bir OOB-read info-leak yazmak yerine buradan besleniyordu.

Warning

Leak edilen değerler canlı runtime adresleridir; sabit değil. Yani telemetri açısından "bilinen kötü bir sabit" aramak işe yaramaz — sinyal, class'ın kullanımının kendisidir.

Detection

  • ETW THREATINT sensor'u. Windows 11 23H2+ çekirdeği, NtQuerySystemInformation (ve NtSystemDebugControl) çağrılarında kernel-address sızdıran class'lar için THREATINT_PROCESS_SYSCALL_USAGE event'ini üretir. Event yalnızca non-admin user-mode caller için ve her class başına process başına bir kez fire eder; bu, EDR için düşük-gürültülü bir "bu process kASLR recon yapıyor" sinyalidir.
  • Behavioral anomali. Meşru bir uygulamanın SystemModuleInformation + SystemHandleInformation + SystemBigPoolInformation gibi bir dizi leak-y class'ı arka arkaya sorgulaması nadirdir; özellikle ardından bir driver open / write-primitive davranışı gelirse chain habercisidir.
  • Non-admin big-pool sorgusu. SystemBigPoolInformation / SystemExtendedHandleInformation gibi çoğunlukla debugger/diagnostic tooling'e ait class'ların medium-IL, non-SeDebug bir process'ten gelmesi telemetride flag'lenmelidir.
  • Guard/CFG bağlamı. Leak'i takip eden indirect-call veya SMEP/CFG ihlali, host-based exploit-guard log'larında ikinci bir korelasyon noktasıdır.

Mitigation

  • 24H2 kernel-address restriction. Windows 11 / Windows Server 2022 24H2 ile, Feature_RestrictKernelAddressLeaks etkinken bu class'lar artık Medium IL için de adres alanlarını 0'lar; tek istisna SeDebugPrivilege'in enable edilmiş olmasıdır (SeSinglePrivilegeCheck(SeDebugPrivilege, ...)), ki bu default'ta yalnızca admin process'lerde bulunur. Böylece sandbox'sız medium-IL leak yolu kapanır.
  • Below-Medium çoktan kapalı. Windows 8.1'den beri ExIsRestrictedCaller Low/Untrusted/AppContainer caller'lar için alanları zero'lar — browser/document sandbox'ları için leak zaten yıllardır ölüydü.
  • Least privilege. Servisleri gereksiz SeDebugPrivilege olmadan çalıştırmak, 24H2 gate'inin bypass edilmesini engeller.
  • Defense-in-depth. kCFG / HVCI, sızan bir pointer downstream'de indirect-call hijack'e dönüşse bile hedef kümesini daraltır; leak'i tümüyle önlemese de exploit değerini düşürür.

References