Skip to content

Stack Pivoting

rsp'yi attacker'ın kontrol ettiği bir buffer'a yönlendirerek bir fake stack / ROP chain çalıştırmak; gerçek overflow alanı tam chain'i tutamayacak kadar küçük olduğunda kullanılır.

Mechanism

Invariant

ret, pop ve leave, rsp'nin gösterdiği her ne ise onu güvenilir control data olarak ele alır: ret, [rsp]'e atlar, pop reg, [rsp]'i tüketir ve leave, mov rsp, rbp; pop rbp yapar. Bu instruction'ların hiçbiri rsp'nin hâlâ meşru call stack'i gösterdiğini doğrulamaz. Stack pivoting bunu silaha dönüştürür: attacker rsp'ye kontrol ettiği bir adres yükleyebilirse, sonraki her ret/pop gerçek stack yerine attacker'a ait bir fake stack'i dolaşır.

Bu önemlidir çünkü güvenlik açığı olan bir buffer çoğu zaman saved return address'ten sonra yalnızca birkaç byte bırakır — iki ya da üç halkalı bir chain için yeterli, ama tam bir ROP payload için değil. Attacker, gerçek (uzun) chain'i başka bir yere saklar (bir bss buffer, daha önceki bir read, bir environment variable) ve rsp'yi o daha büyük region'a işaret eden tek bir pivot gadget çalıştırmak için bu minik pencereyi kullanır. Oradan tam chain çalışır.

Walkthrough

Kavramsal akış; ROP Emporium'un pivot challenge'ını ve herkese açık ctf writeup'larını takip eder. Yaygın pivot gadget'ları ve her birinin ne yaptığı:

Gadget Etki Kaynak register
pop rsp; ret rsp[rsp] sonraki stack qword'ü
xchg rax, rsp; ret rax/rsp swap'ı rax (pop rax ile ayarlanır)
leave; ret rsp ← rbp; pop rbp kontrol edilen rbp
add rsp, N; ret rsp'yi N kadar kaydır sabit offset
# Small overflow: only room for a 3-link chain.
[ saved_rip ] -> pop rax ; ret      # rax = address of large fake stack
              -> xchg rax, rsp ; ret # rsp now points into the fake stack
              -> (execution continues at the big ROP chain stashed earlier)

leave; ret varyantı özellikle yaygındır: birçok fonksiyon leave; ret ile biter, dolayısıyla saved rbp'yi (fake_stack - 8) ile overwrite ederek epilogue'un mov rsp, rbp'si rsp'yi attacker'ın buffer'ına oturtur ve sonraki ret fake chain'e girer ("stack migration").

Pivot gadget'larını bulma

$ ROPgadget --binary ./pivot | grep -E 'xchg .*rsp|pop rsp|leave ; ret|add rsp'
0x...: xchg rax, rsp ; ret
0x...: leave ; ret
0x...: pop rsp ; ret
Attacker ayrıca fake stack için bilinen bir adrese ihtiyaç duyar — genellikle bir leak ile elde edilir (bkz. address-leak) ya da non-PIE bir binary'deki sabit bir bss region'ı ile.

Detection

  • rsp'nin thread'in eşlenmiş stack'i olmayan bir region'a (heap, bss, mmap'lı buffer) atlaması klasik bir anomalidir — EDR / donanımdaki (Intel CET shadow stack) stack-pointer-vs-stack-base kontrolleri bunu işaretler.
  • Hedefine, shadow stack ile eşleşmeyen bir rsp değeri üzerinden ulaşılan bir ret/call, bir control-protection (#CP) fault'u tetikler.
  • Bir fonksiyon epilogue'undan sonra rsp'nin heap/data segment'lerine işaret ettiğini gösteren crash forensics, güçlü bir post-mortem sinyaldir.

Mitigation

  • Intel CET shadow stack return address'lerin korumalı bir kopyasını tutar; rsp'yi saptıran ve forge edilmiş bir adrese ret eden bir pivot, shadow stack ile uyuşmaz ve fault verir.
  • Stack canary'ler (FORTIFY/-fstack-protector) genellikle bir pivot'tan önce gelen lineer overflow'u tespit eder.
  • ASLR bir leak olmadığı sürece attacker'dan bilinen bir fake-stack adresini esirger; NX fake stack'i shellcode yerine bir ROP chain tutmaya zorlar (pivot'un kendisini durdurmaz, ama sonrasında ne geleceğini kısıtlar).

References