ret2plt¶
Bir binary'nin Procedure Linkage Table'ına dönerek import edilmiş bir libc fonksiyonunu (örn.
system,puts) bilinen, rastgeleleştirilmemiş PLT stub'ında çağır — libc base'ini önce leak etmeden libc çağırmak.
Mechanism¶
Invariant
Dinamik link'lenmiş bir program system, puts vb.'yi doğrudan içermez; onları PLT (Procedure Linkage Table) içindeki küçük stub'lar üzerinden çağırır. Her PLT stub'ı, dynamic loader'ın fonksiyonun gerçek libc adresiyle (lazy ya da eager) doldurduğu ilgili GOT (Global Offset Table) girdisi üzerinden atlar.
Anahtar asimetri: bir non-PIE binary'de, libc'nin kendisi ASLR-rastgeleleştirilmiş olsa da PLT ve GOT, executable'a gömülü sabit adreslerde yaşar. Dolayısıyla return address'i kontrol eden bir attacker doğrudan puts@plt'ye ya da system@plt'ye ret edebilir ve programın kendi loader'ı asıl libc target'ını resolve eder — zaten import edilmiş bir fonksiyonu çağırmak için libc base leak'i gerekmez. Sınır, PLT'nin güçlü libc işlevselliğine stabil, attacker-erişilebilir bir entry point sunması nedeniyle aşılır.
Bundan iki yaygın kullanım çıkar:
- Direct call:
system(ya da başka faydalı bir fonksiyon) import edilmişse, kontrollü bir ilk argümanla (örn. binary'de zaten olan bir/bin/shstring'i)system@plt'ye dön. - Leak primitive: bir GOT girdisinin runtime değerini yazdırmak için
puts@plt(puts@got)çağır; bu değerputs'un libc adresine eşittir — takip eden bir ret2libc için bir libc base leak verir. Ayrıca bkz. ret2plt-aslr-bypass ve ret2got.
Walkthrough¶
# conceptual, non-PIE x86-64
# (a) direct: call an imported system("/bin/sh")
rop = b'A'*offset
rop += p64(pop_rdi) + p64(binsh_addr) # arg1 = "/bin/sh" string in the binary
rop += p64(plt_system) # ret into system@plt
# (b) leak: make puts print its own GOT (libc) address, then return to main
rop = b'A'*offset
rop += p64(pop_rdi) + p64(got_puts) # arg1 = &puts@got
rop += p64(plt_puts) # puts(puts@got) -> prints libc addr of puts
rop += p64(main_addr) # loop back to overflow again with libc base known
(a) için neden leak gerekmez
PLT stub adresi executable'ın sabit layout'unun parçasıdır (non-PIE). Onu çağırmak loader'ın normal resolution path'ini tetikler, böylece attacker bir libc adresini kendisi hesaplamak yerine programın kendi dynamic linking'ine biner.
Sınırlar
- Yalnızca hedefin import ettiği fonksiyonların bir PLT stub'ı vardır. Import edilmemiş bir fonksiyonu çağırmak için yine bir libc base'e (sonra ret2libc) ya da ret2dlresolve'ye ihtiyacın var.
- PIE, PLT/GOT'u da rastgeleleştirir, dolayısıyla bu, binary base'inin önceden bir leak'ini gerektirir.
- Üç-argümanlı call'lar hâlâ
rdxkontrolü gerektirir (bkz. ret2csu).
Detection¶
- Beklenmedik bir call site'tan / attacker-şekilli bir argümanla çağrılan GOT/PLT-resolve edilmiş bir libc fonksiyonu (örn.
system,execve) güçlü bir sinyaldir; EDR,/bin/sh'in spawn'ını önceki bir overflow ile ilişkilendirebilir. - Bir
.gotadresinin içeriğini yazdıranputs/write(leak pattern'i), pointer boyutunda data'nın anomalik çıktısı olarak görünür. - Stack canary'ler / ASan upstream overflow'u yakalar.
Mitigation¶
- PIE + ASLR, PLT/GOT'u rastgeleleştirir, böylece stub'lar bir leak olmadan bilinen adreslerde olmaz.
- Full RELRO, GOT'u startup'ta resolve edip read-only map'ler ve ilgili GOT-overwrite varyantlarını (got-overwrite) körleştirir.
- Intel CET shadow stack / IBT ve CFI, PLT'ye olan ROP return'ünü kırar.
- Import edilen tehlikeli fonksiyonları minimize et; erişilen bir fonksiyonun ne yapabileceğini kısıtlamak için
seccomp.
References¶
- Ret2PLT (manjoos pwn notes)
- ret2plt — ASLR Bypass via PLT/GOT Leak (Ian.nl)
- sharkmoos — Binary Exploitation: Exploiting Ret2Libc
Ayrıca bkz: ret2libc, ret2plt-aslr-bypass, ret2got, ret2dlresolve, got-overwrite.