ret2reg¶
retanında bir register (genelderax) attacker'ın buffer'ına işaret ettiğinde, birjmp reg/call reggadget'ı 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-
ENDBRtarget'lara indirectjmp/call'ı reddeder ve register-spring gadget'larını öldürür; shadow stack gadget'a olanret'i kırar. - ASLR/PIE ki gadget adresi bir leak olmadan bilinmesin.
- Compiler CFI, indirect-branch target'larını kısıtlar.
References¶
- HackTricks — Ret2esp / Ret2reg
- The CTF Recipes — ret2reg
- ir0nstone — Using ret2reg
- Wikipedia — Register spring
Ayrıca bkz: return-oriented-programming, ret2mprotect, stack-pivot, virtualprotect-mprotect-via-rop.