Hypercall fuzzing (vmcall handler fuzzing)¶
Bir hypervisor'ın hypercall / VMCALL(Intel) / VMMCALL(AMD) handler interface'inin coverage-guided fuzzing'i ile privileged call surface'inde guest-to-host memory-safety bug'larını bulma.
Mechanism¶
Note
Hypercall, bir guest'in hypervisor'ın içine call yapmasının mimari olarak
tanımlanmış yoludur. Intel'de VMCALL instruction'ı, AMD'de VMMCALL'dır;
Hyper-V opcode'u guest'e map'lenmiş bir hypercall page arkasında soyutlar,
böylece guest sadece onun ilk byte'ını CALL'lar. Her durumda CPU bir VM-exit
alır ve hypervisor, guest'in kontrol ettiği bir call code'a göre dispatch
eder. Microsoft TLFS'e göre guest, RCX içinde 64-bit bir hypercall input
value geçirir: bit 15-0 call code, bit 16 Fast flag'i (0 = memory /
"slow", parametreler bir guest-physical address'te durur; 1 = register /
"fast"), bit 26-17 bir variable-header size, bit 43-32 bir rep count'tur. Slow
call'lar için RDX input-parameter GPA'sını, R8 output GPA'sını tutar; fast
call'lar için RDX/R8 (ve 112-byte'lık XMM-fast formu için XMM0-5)
parametreleri doğrudan taşır. Sonuç RAX içinde bir HV_STATUS code olarak
geri döner.
Bir fuzzer'ın suistimal ettiği invariant tam olarak budur: call code ve
parameter block tamamen guest-attacker-controlled'dur ve handler, sistemdeki
en yüksek privilege boundary'sinde çalışır. Eğer bir handler gömülü bir
length, GPA veya rep count'a validation yapmadan güvenirse, hatalı biçimlenmiş
bir input value host içinde bir OOB read/write'a yol açar. Fuzzing, o
handler'lara ulaşmak için RCX'i artı parameter block'u sistematik olarak
mutate eder.
Walkthrough¶
Microsoft TLFS hypercall interface'ine ve SafeBreach/Guardicore'un hAFL1'ine (kAFL tabanlı Hyper-V fuzzer) dayanır.
- ABI'yi konuş. Guest tarafındaki bir harness, interface'i TLFS'in öngördüğü
şekilde kurar:
CPUID 0x40000001ileHv#1'i doğrula, Guest OS ID MSR'ını (HV_X64_MSR_GUEST_OS_ID, 0x40000000) yaz, sonra hypercall page GPFN'ini + enable bit'iniHV_X64_MSR_HYPERCALL'a (0x40000001) programla. Ardından ham bir input value birleştirilir, örneğin Fast bit'i set edilmiş0x0008call code'u:
// memory-based ("slow") hypercall: input/output via GPAs
uint64_t input_value = (call_code & 0xFFFF); // Fast=0 => slow
// RCX = input_value, RDX = input_gpa, R8 = output_gpa, then CALL hv_page
Fuzzer'ın mutation target'ı {input_value, *input_gpa}'dır.
-
Coverage-guided bir harness altında sür. hAFL1, modifiye edilmiş bir kAFL'dir. Burada iki ayrı hypercall rolünü karıştırmamak gerekir: (1) control channel — L0'daki kAFL/Nyx fuzzer, fuzzing payload'unu guest user-mode harness'a kAFL'in kendi agent-protocol hypercall'ları (guest↔KVM sync/payload handoff) üzerinden teslim eder; harness sonra bunu IOCTL ile bir kernel harness driver'ına (
Harness.sys) iletir. (2) attack surface — asıl mutate edilen şey ise target hypervisor'ın hypercall input value/parameter block'udur, yani harness'ın hedefe issue ettiği hypercall'ın kendisi. Yani kAFL'in transport hypercall'ı, fuzz'lanan Hyper-V hypercall'ı ile aynı şey değildir.loader.exeher crash'ten sonra dönülecek VM state'inin snapshot'ını alır; coverage feedback Intel-PT'den gelir. hAFL1vmswitch.sys'i (VMBus üzerinden RNDIS) hedefler ve libprotobuf-mutator ile structure-awareness ekler, ama aynı harness pattern'i generic hypercall dispatcher'ı da fuzz'lar. -
Snapshot + restore loop. Hatalı bir hypercall host state'ini bozabildiği için loop snapshot tabanlıdır: restore et, bir mutate edilmiş input inject et, çalıştır, exit/crash'i yakala, tekrarla — kAFL/Nyx modeli.
-
Beklenen çıktı. Çoğu input
RAXiçinde zararsız birHV_STATUSdöner (örneğin bilinmeyen bir call code içinHV_STATUS_INVALID_HYPERCALL_CODE, bozuk bir rep count / reserved bit içinHV_STATUS_INVALID_HYPERCALL_INPUT). Bir bug ise kendini host hang, bir hypervisor bugcheck, ya da bir handler'a giden Intel-PT ile doğrulanmış yeni bir path (sonrasında fault'lanan) olarak gösterir — fuzzer'ın triage ettiği sinyal budur.
Warning
Reserved-bit ve alignment check'leri (HV_STATUS_INVALID_ALIGNMENT, 8-byte
GPA alignment, page-crossing parameter list yasağı) naif mutation'ların büyük
bir kısmını erkenden reddeder. Input value layout'una ve call-code başına
parameter struct'larına saygı gösteren structure-aware bir mutator,
handler'lara RCX'i byte-flip'lemekten çok daha derin ulaşır.
Detection¶
- Host/defender tarafından: anormal oranda başarısız olan
HV_STATUScode'ları, alışılmadık call-code dağılımları ya da tek bir partition'dan gelen tekrarlı invalid-input dönüşleri, fuzz'layan veya yoklayan bir guest'in güçlü göstergeleridir. Hyper-V/KVM hypercall failure'larını VP başına rate-limit'leyip loglayabilir. - Crash telemetry: snapshot-restore fuzzer'ları host tarafında patlamalar halinde fault ve VM reset üretir; tek bir guest'e bağlı kümelenmiş bugcheck'ler bir ipucudur.
- Timing: snapshot/restore ve Intel-PT mekanizması guest timing'ini bozar; bu, wall-clock'u retired-cycle drift'ine karşı karşılaştıran guest içindeki bir watcher tarafından gözlemlenebilir.
Mitigation¶
- Hypercall input value'nun guest tarafından kontrol edilen her field'ını (call code aralığı, Fast flag'i, variable-header size, rep count/start) parameter block'a dokunmadan önce, tam olarak TLFS common status code'larının öngördüğü gibi validate et.
- Input/output GPA'larını untrusted kabul et: her erişimde mapped/readable/ writable'ı yeniden validate et ve alignment ile page-crossing yasağını zorla.
- Dispatch table'ı minimal tut ve privileged call code'ları partition privilege'larına göre gate'le; ship etmeden önce kendi surface'ini fuzz'la (KVM için syzkaller, Hyper-V için hAFL1/kAFL/Nyx).
References¶
- Hypercall Interface — Microsoft TLFS (Microsoft Learn)
- Hypervisor Top Level Functional Specification (MicrosoftDocs/Virtualization-Documentation)
- hAFL1 — Fuzzing Hyper-V and discovering a 0-day (Akamai/Guardicore)
- SB-GC-Labs/hAFL1 (GitHub)
- hAFL2 — The First Open-Source Hypervisor Fuzzer (SafeBreach)