Skip to content

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:

b8 ZZ YY XX a8        mov $0xa8XXYYZZ, %eax

İkinci byte'a atlamak, CPU'nun ZZ YY XX'i bir payload instruction olarak decode etmesini, ardından a8 b8test $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_enable sysctl'i JIT'i kontrol eden anahtardır (JIT'i açıp kapatır).
  • net.core.bpf_jit_harden = 2 root için bile constant'ları blind eder.
  • CONFIG_BPF_JIT_ALWAYS_ON / hardening config'leri JIT'i daha da kısıtlar.

References