Skip to content

ret2reg

ret anında bir register (genelde rax) attacker'ın buffer'ına işaret ettiğinde, bir jmp reg / call reg gadget'ı ile execution'ı oraya yönlendir — sabit bir shellcode adresi gerekmez.

Mechanism

Invariant

Control flow'u enjekte edilmiş koda yönlendirmek için bir attacker'ın normalde o kodun adresine ihtiyacı vardır — ki ASLR/PIE bunu gizler. ret2reg ("register spring") adres problemini yan geçer: control-flow hijack noktasında bir register zaten attacker'ın buffer'ının içine bir pointer tutuyorsa, tek bir jmp reg ya da call reg gadget'ı execution'ı oraya attacker mutlak adresi hiç bilmeden transfer eder.

Bu genelde calling convention'dan doğar: bir fonksiyonun return value'su rax/eax'e yerleştirilir. gets, strcpy, fgets gibi input-işleyen fonksiyonlar, tam da yeni doldurdukları buffer'a bir pointer döndürür — dolayısıyla döndükten hemen sonra rax attacker verisine işaret eder. jmp rax gibi bir gadget (bir "register spring") o zaman execution'ı o buffer'a indirir. Sınır, bilinmeyen bir mutlak adresin yerine stabil bir register-relative entry point geçtiği için aşılır. rsp-tabanlı kuzeni (jmp rsp / jmp esp) aynı fikrin stack'e nişanlanmış hâlidir — bazen ret2esp olarak ayrılır.

Walkthrough

# conceptual x86-64: rax points at the buffer after gets()/strcpy()
# find a gadget:  ROPgadget --binary ./target | grep 'jmp rax'
payload  = shellcode                 # placed at the start of the buffer (rax -> here)
payload += b'A'*(offset-len(shellcode))
payload += p64(jmp_rax_gadget)       # overwrite saved RIP; on ret -> jmp rax -> shellcode

Bir gadget finder ile bulunan temsili gadget formları:

jmp rax            # primary register spring
call rax
jmp rsp            # ret2esp variant: rsp points at stack-resident shellcode
rax neden alışılmış tercihtir

System V / cdecl convention'a göre return value rax/eax'e iner. User input'a bir pointer döndüren fonksiyonlar (gets, strcpy, fgets), rax'i buffer'a nişanlı bırakır ve jmp rax'i güvenilir bir spring yapar. Bilinen bir register kontrollü veriye işaret ettiğinde başka register'lar da çalışır, ama onlar için uygun jmp/call <reg> gadget'ları daha kıt olabilir.

Executable target bellek gerektirir

ret2reg yalnızca control flow'u yönlendirir. Hedef page non-executable (NX/DEP) ise, jump iner ama shellcode çalışamaz — onu izin-değiştiren bir aşamayla (ret2mprotect / virtualprotect-mprotect-via-rop) eşle ya da zaten-executable belleğe nişanla. jmp/call reg gadget'ının kendi adresi hâlâ ASLR/PIE'ye tabidir ve bir leak gerektirebilir; CET shadow stack gadget'a ulaşan ret'i bloklayabilir.

Detection

  • Bir register üzerinden bir jmp/call'tan hemen sonra control flow'un stack/heap'e (yazılabilir data page'leri) transfer olması — meşru kod için atipiktir; CFI/IBT ve DEP buna fault verir.
  • Bir return value register'ının işaret ettiği bir input buffer'ının başındaki shellcode-benzeri byte pattern'leri.
  • Stack canary'ler / ASan upstream overflow'u yakalar.

Mitigation

  • NX/DEP, register'ın işaret ettiği buffer'ın çalışmasını engeller (en doğrudan savunma).
  • Intel CET — IBT, non-ENDBR target'lara indirect jmp/call'ı reddeder ve register-spring gadget'larını öldürür; shadow stack gadget'a olan ret'i kırar.
  • ASLR/PIE ki gadget adresi bir leak olmadan bilinmesin.
  • Compiler CFI, indirect-branch target'larını kısıtlar.

References

Ayrıca bkz: return-oriented-programming, ret2mprotect, stack-pivot, virtualprotect-mprotect-via-rop.