Skip to content

LeJIT (JIT spray extended to ARM)

"Too LeJIT to Quit": JIT spraying'i x86'dan ARM'ın fixed-width ISA'sına taşıyan ve x86 saldırısını durduran constant-blinding savunmalarını yenen bir gadget-chaining JIT-spray tekniği.

Mechanism

Neden çalışır

Klasik JIT spray, x86'nın variable-length, unaligned instruction stream'ini suistimal eder: JIT tarafından yayılan saldırgan-seçimi sabitler (örn. 0x3c909090 immediate'ları), instruction-ortası bir offset'ten amaçlanmamış, executable bir payload olarak yeniden decode edilebilir. ARM bir fixed-width RISC ISA olduğu için bağışık varsayılıyordu — 4-byte'lık bir instruction'ın ortasına atlayıp farklı bir tane elde edemezsin.

LeJIT bu varsayımı gadget chaining ile kırar: tek bir yeniden yorumlanan stream yerine, "high level kod, tıpkı bir fonksiyon çağrısı gibi gadget denen kısa amaçlanmamış ve amaçlanmış instruction dizileri invoke eder" ve birçok küçük JIT-yayılı gadget'ı bir payload'a zincirler. Anahtar primitive, 32-bit ARM/Thumb-2 ile 16-bit Thumb encoding'leri arasındaki overlap'i kullanır: JIT'i bir 32-bit bitwise AND instruction'ı yaymaya zorlayarak (saldırgan bunun ~20 bit'ini kontrol eder), aynı byte'lar iki 16-bit Thumb-2 instruction'ı olarak yeniden decode olur — ilki saldırgan için faydalı iş yapar, ikincisi bir sonraki gadget'a unconditional PC-relative forward branch olarak davranır.

Saldırganın geri kazandığı invariant: fixed-width bir ISA'da bile, saldırgan-etkili immediate field'ları artı alternatif (Thumb) decoding, kontrol edilebilir, kendi kendine branch eden bir instruction stream'i verir — yani writable-and-executable bir JIT bölgesindeki W^X / DEP yeterli değildir.

Walkthrough

Bu, JIT mitigation'larının defense/bypass alanında yaşadığı için burada kataloglanan offansif bir tekniktir. Yayınlanmış proof of concept, ARM'da WebKit'in JavaScriptCore'unu hedefler.

1. Script'ten constant emission'ı zorla. JS arithmetic / bitwise op'ları, baseline JIT'in saldırgan-kontrollü immediate'lar taşıyan instruction'lar yaymasına neden olur:

// JS that drives JavaScriptCore to emit 32-bit AND with a chosen immediate
var x = obj & 0x0001abcd;   // immediate bits become payload bytes

2. Bir 32-bit AND'i iki Thumb-2 halfword olarak yeniden yorumla. Yayılan 4-byte ANDS, bir halfword içeriden girilince şöyle decode olur:

[ 32-bit ARM/Thumb-2 ANDS  ]   <- intended encoding
 ^hw0          ^hw1
 |             |
 |             +--> hw1: unconditional PC-relative forward branch (-> next gadget)
 +----------------> hw0: attacker-useful 16-bit Thumb instruction

Bunu tekrarlamak kendi kendini sürdüren bir gadget chain verir: her gadget iş yapar, sonra bir sonrakine forward branch eder ve sprayed JIT buffer'ından geçer.

x86 dönemi mitigation'ları burada neden başarısız olur

JavaScriptCore constant blinding gönderdi; bu, geniş immediate'ları rastgele bir cookie ile XOR'lar, böylece saldırgan tarafından seçilemezler. LeJIT'in gadget'ları "constant'lara dayanmayacak" (ya da yalnızca 1-byte constant'lara dayanacak) şekilde üretilir, yani geniş immediate'ları blind'lamak chain'i bozulmadan bırakır — "x86 sürümüne karşı yeterli olan mevcut JIT spray mitigation'ları ... yetersiz kalır."

Detection

Defensive sinyaller: anomal JIT-region içeriği (tekrarlı emit-immediate desenlerinin uzun dizileri / yüksek gadget yoğunluğu), bir JIT page'i içinde beklenmedik kontrol transferleri ve JIT bölgesi hiç non-writable-while-executable yapılırsa W^X ihlalleri.

Mitigation

(Bypass context / residual savunmalar.) Constant blinding yetersiz olduğu için, sağlam savunmalar JIT yapısına kayar: ayrı bir writable alias'lı (dual mapping) per-page W^X artı küçük immediate'ların bile constant blinding'i, gadget alignment'ını bozmak için code randomization / NOP insertion ve writable+executable penceresini tamamen kaldırmak (örn. out-of-process ya da hardened JIT'ler). İlgili varyantlar için constant-embedded-gadget-jit-spray ve asm.js-targeted JIT spray'e, LeJIT'in etrafından dolaştığı baseline W^X varsayımı için DEP/NX'e bak.

References