VirtualProtect/mprotect via ROP (DEP disable)¶
Bir ROP chain'i ile
mprotect(Linux) ya daVirtualProtect(Windows) çağırıp attacker-controlled bir region'ı executable'a çevirmek, ardından artık çalıştırılabilir shellcode'a jump etmek — NX/DEP'i yenmek.
Mechanism¶
NX/DEP, data page'lerini (stack, heap) non-executable işaretler, böylece enjekte
edilen shellcode doğrudan çalışamaz. Ama page izinlerine karar veren kodun
kendisi — mprotect/VirtualProtect — meşru, executable ve erişilebilirdir.
Return-oriented programming, mevcut gadget'ları zincirleyerek argümanları
hazırlar ve o function'ı çağırır; OS'ten shellcode'un page'ini executable
yapmasını ister. Page bir kez RWX olduğunda, kontrol oraya transfer edilir ve
native kod çalışır. Chain bir one-shot bootstrap'tır: ROP'un yalnızca bir
function çağırıp pivot edecek kadar hayatta kalması yeterlidir.
Note
Argüman geçirme ayrıntısı işin tüm hilesidir. Linux x86-64'te SysV ABI
argümanları rdi, rsi, rdx'e koyar; mprotect(void *addr, size_t len, int prot)
için rdi=page-aligned addr, rsi=len, rdx=7 (PROT_READ|WRITE|EXEC) gerekir.
Windows x64'te fastcall ABI rcx, rdx, r8, r9 kullanır; VirtualProtect
ek olarak lpflOldProtect'in (4. argüman) writable bir dword'ü
göstermesini ister, yoksa call başarısız olur.
Walkthrough¶
mprotect page-aligned bir adres ister ve PROT_READ(1), PROT_WRITE(2),
PROT_EXEC(4) kabul eder — yani prot = 7 RWX'tir. Kavramsal x86-64 chain
(adresler örnektir):
# goal: mprotect(buf & ~0xfff, 0x1000, 7); jmp buf
pop rdi ; ret -> 0x404000 # page-aligned target page
pop rsi ; ret -> 0x1000 # length
pop rdx ; ret -> 0x7 # PROT_READ|PROT_WRITE|PROT_EXEC
mprotect -> &mprotect # call it
<addr of shellcode in that page> # return into now-RWX shellcode
pwntools ile inşa etmek (örnek amaçlı, silah haline getirilmemiş):
from pwn import *
rop = ROP(elf)
rop.mprotect(page_base, 0x1000, 7) # marks the page RWX
rop.raw(shellcode_addr) # return into the RWX page
payload = b"A"*offset + rop.chain()
Windows'ta eşdeğeri
VirtualProtect(lpAddress, dwSize, PAGE_EXECUTE_READWRITE /*0x40*/, &oldProtect)
çağırır; 4. parametre geçerli writable bir değişkeni göstermek zorunda olduğundan,
chain lpflOldProtect için (çoğu zaman .data'da) bir scratch dword reserve eder
ve API'ye göre, region'ı çalıştırmadan önce cache coherency için
FlushInstructionCache ile devam etmelidir.
mprotect(2) / VirtualProtect signature'ları
Mitigation¶
Function-call ROP chain'leri CET shadow stack'ler (return-address bütünlüğü)
ve IBT/endbr (indirect-branch landing pad'leri) ile körelir. W^X policy artı
sertleştirilmiş loader'lar ve SELinux/grsecurity mprotect kısıtlamaları
(writable mapping'lerde PROT_EXEC reddi), page flip'i engeller. ASLR,
mprotect/VirtualProtect'i ve gadget'ları bulmak için önceden bir
address-leak yapmayı zorunlu kılar.