Skip to content

Call-Oriented Programming (COP)

Indirect CALL instruction'larında (RET değil) sonlanan gadget'ları zincirle; CFG-legal call edge'lerinde kalarak return-address savunmalarını ve coarse-grained CFI'yı bypass et.

Mechanism

Note

Return-address savunmaları (shadow stack'ler, kBouncer/ROPGuard "call-preceded" check'leri) yalnızca return edge'ini kısıtlar. Carlini et al. (USENIX 2015) bir shadow stack hakkında belirtir ki "attacker'ın return instruction'ları üzerinde çok sınırlı etkisi vardır … return instruction'larının keyfi başka konumlara dönmesine yol açamaz." COP bunu, control flow'u o savunmaların kısıtlamadığı indirect call'lar üzerinden sürerek aşar. Davi et al. (USENIX 2014) coarse-grained CFI'nın yalnızca "return address'in bir call instruction'ından sonra gelen bir instruction'a işaret etmesini" (herhangi bir call-preceded site) gerektirdiğini ve "indirect call pointer'larının integrity'sinin doğrulanmadığını" gösterir.

İki yapısal parça COP'u işler kılar:

  • Call-site gadget'ları — bir indirect call ile biten diziler (örn. mov %esi,(%esp); call *%edi); adresi attacker-controlled memory/register'lardan gelen bir target'ı çağırır.
  • Dispatcher function — Carlini'nin COP'taki, JOP'un dispatcher gadget'ının analoğu: "bir dispatcher function, attacker tarafından sağlanan argümanlar verildiğinde kendi return address'ini overwrite edebilen bir fonksiyondur." Bir write-what-where sunan herhangi bir fonksiyon (memcpy, %n üzerinden printf, strcat) gadget'ları zincirlerken her transfer CFG-legal bir edge olarak kalır.

Warning

COP, return-address korumasından kaçar çünkü asla corrupt olmuş bir return edge'ine dayanmaz — zincirleme, savunmanın izlemediği ya da yalnızca kabaca check ettiği indirect-call edge'leri üzerinden olur. JOP (indirect jump'lar) ve COOP (C++ virtual call'lar) ile ilişkilidir ama onlardan ayrıdır.

COP ≠ PCOP. Bu girdi, Carlini et al.'nin (2015) coarse-grained CFI bypass argümanını anlatır; dispatcher burada kendi return address'ini overwrite eden bir fonksiyondur (memcpy/printf %n). Buna karşılık Pure-Call Oriented Programming (PCOP) (Sadeghi & Niksefat, 2017) farklı bir akademik modeldir: her fonksiyonel gadget bir indirect call ile biter ve katkı, push edilen return-address side effect'lerinin nötralize edilip yalnızca call-terminated glibc gadget'larıyla Turing-completeness'in gösterilmesidir.

Walkthrough

memcpy'yi dispatcher olarak kullanan pattern (Carlini'nin invariant'ı):

// Attacker controls all args to a later call: memcpy(dst, src, n)
dst = &(memcpy's own return address on the stack);
src = attacker_buffer;     // holds the chosen "next location"
n   = sizeof(void*);
// memcpy() overwrites its own return address, then returns to an
// attacker-chosen call-preceded instruction => an allowed CFG edge.

Beklenen davranış (Carlini'ye göre): "memcpy() kendi return address'ini overwrite eder ve sonra başka bir konuma döner … Bu başka konum geçerli CFG'de ise (yani memcpy()'ye yapılan bir call'ı izleyen bir instruction), o izin verilen bir edge'dir ve CFI return'e izin verir." Bunu iterate etmek — Davi'nin GOT-overwrite edilmiş indirect call'lar üzerinden Call-Ret-Pair gadget'ları — coarse-grained call-preceded policy'sini hiç ihlal etmeden ve bir shadow stack'in yakalayacağı return-address sahteciliği olmadan execve/VirtualAlloc'a ulaşır. ROP / JOP gadget discovery'sinin üzerine kurulur.

Detection

  • Indirect-call target'larını gerçek call signature'ına karşı doğrulayan fine-grained / forward-edge CFI, call-site gadget chain'ini kırar.
  • Indirect-branch tracking (Intel CET IBT), her indirect-call target'ında bir ENDBR landing pad'i gerektirir ve çoğu mid-function gadget'ını ortadan kaldırır.

Mitigation

  • Forward-edge CFI (clang -fsanitize=cfi) + CET IBT, indirect-call destination'larını kısıtlar.
  • Full RELRO, indirect call'ları yönlendirmek için kullanılan GOT-overwrite ayağını ortadan kaldırır.
  • Coarse-grained, yalnızca call-preceded policy'ler yetersizdir — bu, paper'ın merkezi noktasıdır.

References