Skip to content

Block-Oriented Programming (BOP/BOPC)

Tek bir memory-write primitive'inden valid execution path'leri boyunca tüm basic block'ları zincirleyerek data-only saldırıları otomatikleştirir; CFI veya shadow-stack policy'lerini hiç ihlal etmeden bir attacker payload'ını çalıştırır.

Mechanism

Neden çalışır

Control-flow integrity ve shadow stack'ler execution'ın nereye gidebileceğini kısıtlar: indirect branch'ler valid target'lara isabet etmeli, return'ler call stack ile eşleşmelidir. ROP/JOP bu kuralları kırar ve yakalanır. BOP onları kırmaz — programın legitimate control-flow graph'ının içinde kalır.

Anahtar değişim granularity'dir: kısa instruction-suffix gadget'lar yerine BOP, valid path'lerde zaten var olan eksiksiz functional basic block'lar kullanır. Bir payload, küçük bir high-level dil olan SPL ile ifade edilir (Turing-complete, architecture-abstracted). Compiler olan BOPC, input olarak arbitrary bir memory-write ("what-where") primitive alır ve şunları arar:

  • functional block'lar — ihtiyaç duyulan bir hesaplamayı (bir move, bir add, bir syscall setup) yapan basic block'lar, ve
  • dispatcher block'lar — valid CFG edge'lerinden ayrılmadan execution'ı bir functional block'tan sonrakine yönlendiren block'lar.

BOPC bunları bir execution trace'ine diker ve programın doğal entry'sinden başlayarak binary'nin saldırganın SPL payload'ını çalıştırması için yerleştirilmesi gereken memory write setini çıkarır. Alınan her edge, CFI policy'sinin zaten izin verdiği bir edge olduğundan, "programda valid execution path'leri boyunca eksiksiz basic block'ları gadget olarak kullanır, yani CFI veya shadow stack policy'lerini ihlal etmeden." CFI'nin dayandığı invariant — saldırgan yalnızca policy'nin yasakladığı control flow'u kullanabilir — geçerli değildir; data-only execution Turing-complete'tir.

Walkthrough

BOPC bir target binary artı bir write primitive üzerinde çalışır:

1. Write the desired behaviour in SPL (e.g. set up and invoke execve):
       __r0 = "/bin/sh";  __r1 = 0;  __r2 = 0;  execve(__r0, __r1, __r2);

2. Run BOPC against the target:
       bopc.py --binary ./target --spl payload.spl --entry <addr>
   It locates functional + dispatcher basic blocks and synthesizes a trace.

3. Output: a set of "what-where" memory writes — the memory state to install
   via the corruption primitive so that, when the program reaches the entry
   point, it walks the chosen blocks and realizes the SPL payload.

Beklenen sonuç: öngörülen memory içeriği yazıldığında, victim'in değiştirilmemiş control flow'u saldırganın hesaplamasını gerçekleştirir — CFI/shadow-stack check'lerinden geçen bir data-only saldırı.

Data-only saldırılar CFI'nin geride kalanıdır

BOP/BOPC, fine-grained CFI ve shadow stack'lerin kararlı exploitation'ı durdurmadığının gösterimidir: kontrollü data yazabilen bir saldırgan, legal path'ler üzerinden kullanışlı bir payload'a ulaşabilir. ROP ile rekabet etmekten çok onu tamamlar — control-flow edge'leri kilitlendikten sonra geriye kalan şeydir.

Mitigation

(Neyin durdurduğu.) Kaynakta memory-safety (write primitive'ini önlemek), data-flow integrity / fine-grained data isolation ve istismar edilebilir block reachability'sini azaltmak. CFI ve shadow stack'ler tek başına block-oriented, data-only saldırılara karşı yetersizdir.

References