Skip to content

SROP + mprotect

Bir signal frame forge et ki tek bir rt_sigreturn, mprotect (veya mmap) syscall numarasını ve tüm argümanlarını tek seferde yüklesin, bir bellek bölgesini RWX'e çevirsin, sonra oraya yerleştirilen shellcode'a jump et.

Mechanism

Invariant

rt_sigreturn, tüm CPU context'ini mevcut rsp'de duran bir sigcontext/ucontext yapısından geri yükler. Kernel bu frame'i authenticate etmez — byte'ların kendi signal-delivery path'i tarafından yazıldığına güvenir. SROP o güveni suistimal eder: forge edilmiş bir frame, bir gadget'ın her general-purpose register'ı (rax, rdi, rsi, rdx, ...) artı rip ve rsp'yi tek adımda set etmesini sağlar (bkz. sigreturn-oriented-programming).

+mprotect varyantı o primitive'i NX'e yöneltir. Bir execve frame'i forge etmek yerine, attacker bir mprotect(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC) frame'i forge eder. sigreturn frame'i rax, rdi, rsi ve rdx'i aynı anda set ettiğinden, bir trigger attacker'ın kontrol ettiği bir page'i RWX'e çevirir. Control flow sonra daha önce o page'e yazılmış shellcode'a transfer olur — küçük veya static bir binary'de kıt olabilen argüman başına pop gadget'larına ihtiyaç duymadan no-execute/DEP sınırını etkisiz kılar.

Walkthrough

Herkese açık CTF writeup'larını (örneğin TAMUctf, DarkCTF) ve pwntools SigreturnFrame API'sini takip eden yüksek seviye yeniden üretim. Zincir iki aşamalıdır:

  1. Triggerrax = 15 (__NR_rt_sigreturn) elde et ve bir syscall instruction'ına ulaş. Yaygın bir path: kontrol ettiğiniz bir read() syscall'ı 15 byte okuyarak (böylece rax=15 set eder) veya 15 yükleyen bir pop rax; ret gadget'ı.
  2. Restore — trigger'ın altındaki forge edilmiş frame, mprotect için context'i yeniden yükler; rip bir syscall gadget'ına geri yöneltilir ki bir sonraki instruction tam da mprotect çağrısı olsun. rsp, execution seçilen bir bölgeye devam edecek şekilde set edilir.
from pwn import *
context.arch = 'amd64'

frame = SigreturnFrame()
frame.rax = constants.SYS_mprotect    # 10
frame.rdi = page_base                 # page-aligned region to mark RWX
frame.rsi = 0x1000                    # length
frame.rdx = 7                         # PROT_READ|WRITE|EXEC
frame.rip = syscall_ret               # the mprotect call itself
frame.rsp = pivot_into_shellcode      # continue here afterward

payload  = b'A'*offset
payload += p64(pop_rax_15) + p64(syscall_ret)   # stage 1: rax=15, trigger sigreturn
payload += bytes(frame)                          # stage 2: forged mprotect context
Neden execve yerine mprotect

execve("/bin/sh"), bilinen bir adreste yazılabilir bir "/bin/sh" string'ine ihtiyaç duyar ve doğrudan bir shell verir. mprotect varyantı, attacker'ın zaten büyük, kontrol ettiği bir buffer'ı olduğunda (örneğin daha önceki bir read ile doldurulmuş bir bss/stack bölgesi) ve sadece bir shell spawn etmek yerine arbitrary shellcode çalıştırmak istediğinde tercih edilir — hedef syscall seti filtrelendiğinde veya daha büyük bir payload hazırlarken yaygındır.

mprotect syscall'u return ettikten sonra bölge RWX'tir; zincir yeniden girer (genellikle rsp'yi pivot ederek veya bilinen bir entry'ye return ederek) ki oraya zaten hazırlanmış byte'lar code olarak çalışsın.

Detection

  • Kernel'in signal trampoline'i olmayan bir return address'inden çağrılan rt_sigreturn (syscall 15) anormaldir; onu strace/auditd syscall auditing'i veya bir eBPF probe ile trace et.
  • Yazılabilir bir bölgede (W^X ihlali) PROT_EXEC isteyen, özellikle stack veya heap'te, hemen ardından o bölgeye bir control transfer'i gelen bir mprotect/mmap çağrısı — güçlü bir EDR sinyali.
  • Stack'te tam bir register reload'unu besleyen sigcontext boyutunda bir blob (~248 byte), memory forensics'te olağandışı bir stack layout olarak görünür.

Mitigation

  • rt_sigreturn, mprotect ve mmap'i reddeden veya kısıtlayan seccomp-BPF allow-list'leri zinciri kırar; mprotect(PROT_EXEC) üzerindeki SECCOMP_RET_KILL etkilidir.
  • W^X zorunlu kıl ki hiçbir mapping aynı anda hem yazılabilir hem executable olmasın; SELinux execmem/execmod politikası yazılabilir belleği executable yapmayı reddeder.
  • Genel anti-corruption savunmaları ilk stack kontrolü için çıtayı yükseltir: stack canary'leri, ASLR ve NX.

References