BPF JIT Spray (ret2bpf)¶
Shellcode byte'larını BPF immediate constant'larının içine encode et; kernel JIT bunları executable kernel memory'ye yerleştirsin, sonra JITlenmiş bir instruction'ın ortasına atlayıp çalıştır — böylece SMEP bypass edilir.
Mechanism¶
Note
Linux BPF JIT, BPF program'larını native x86'ya derler ve module_alloc ile
kernel module bölgesine yerleştirir. Derlenmiş program'lar page-aligned (4 KB)
olduğundan adresler kısmen tahmin edilebilir hale gelir. Attacker'ın seçtiği bir
32-bit constant'ı yükleyen bir BPF "load immediate", mov $imm32, %eax'e derlenir;
yani immediate byte'lar executable kernel memory içinde durur. Execution'ı
instruction'ın ortasına (opcode byte'ından sonrasına) yönlendirerek, CPU
immediate byte'ları farklı bir instruction stream olarak — yani gömülü shellcode
olarak — yeniden yorumlar. Kod kernel space'te yaşadığı için SMEP devreye girmez
(SMEP yalnızca kernel'in userspace page'lerini execute etmesini engeller).
Walkthrough¶
0xa8XXYYZZ constant'ını yükleyen bir BPF mov şunu üretir:
İkinci byte'a atlamak, CPU'nun ZZ YY XX'i bir payload instruction olarak decode
etmesini, ardından a8 b8 → test $0xb8, %al'ı işlemesini sağlar. 0xa8 constant'ı bir
separator gadget gibi davranır; bir sonraki mov'un b8 opcode'unu zararsızca tüketir,
böylece bir BPF immediate zinciri 3-byte'lık payload parçalarını çalışan bir routine'e
birleştirir. Tam bir commit_creds(prepare_kernel_cred(0)) privesc payload'u tamamen bu tür
parçalardan inşa edilebilir.
"ret2bpf" varyantı ayrıca bir UAF sonrası JITlenmiş bir bölgeyi yeniden kullanır: freed memory'ye dangling bir pointer bırak, bu bellek bir JITlenmiş BPF program'ı tarafından geri alınsın (reclaim), sonra control'ü JITlenmiş (executable) byte'lara yönlendir.
Warning
Adresin hâlâ load başına offset/entropy'si vardır; spray, bir jump'ın kontrol edilen bir JITlenmiş page'in içine düşme olasılığını artırmak için birçok program ile doldurur. Constant blinding geldikten sonra (aşağıda) tahmin edilebilirlik keskin biçimde düştü.
Mitigation¶
Constant blinding immediate'ları randomize eder, böylece attacker byte'ları tahmin edemez: JIT, seçilen her immediate'ı rastgele bir constant ile XOR'lar ve kullanmadan önce tekrar XOR'lar; bu da "JITlenmiş kodun ortasına return etme" tekniğini yok eder. Daniel Borkmann tarafından implemente edilmiştir. Kontroller:
net.core.bpf_jit_enablesysctl'i JIT'i kontrol eden anahtardır (JIT'i açıp kapatır).net.core.bpf_jit_harden = 2root için bile constant'ları blind eder.CONFIG_BPF_JIT_ALWAYS_ON/ hardening config'leri JIT'i daha da kısıtlar.