# Relro {{#include ../../banners/hacktricks-training.md}} ## Relro **RELRO**, **Relocation Read-Only** anlamına gelir ve bir bağlayıcı (`ld`) tarafından uygulanan bir önlemdir; bu, ELF'nin veri segmentlerinin bir alt kümesini **tüm yeniden konumlandırmalar uygulandıktan sonra yalnızca okunur hale getirir**. Amaç, bir saldırganın program yürütülmesi sırasında dereferans edilen **GOT (Global Offset Table)** veya diğer yeniden konumlandırma ile ilgili tabloların girişlerini yazmasını engellemektir (örneğin, `__fini_array`). Modern bağlayıcılar, RELRO'yu **GOT'u** (ve birkaç diğer bölümü) **.bss**'den **önce** yerleştirerek ve – en önemlisi – dinamik yükleyici yeniden konumlandırmaları uyguladıktan hemen sonra `R–X` olarak yeniden haritalanan özel bir `PT_GNU_RELRO` segmenti oluşturarak uygular. Sonuç olarak, **.bss**'deki tipik tampon taşmaları artık GOT'a ulaşamaz ve keyfi yazma ilkelere, RELRO korumalı bir sayfanın içinde bulunan işlev işaretçilerini yazmak için kullanılamaz. Bağlayıcı tarafından yayımlanabilecek **iki seviye** koruma vardır: ### Kısmi RELRO * `-Wl,-z,relro` (veya `ld`'yi doğrudan çağırırken sadece `-z relro`) bayrağı ile üretilir. * Sadece **GOT**'un **non-PLT** kısmı (veri yeniden konumlandırmaları için kullanılan kısım) yalnızca okunur segmente konur. Çalışma zamanında değiştirilmesi gereken bölümler – en önemlisi **lazy binding**'i destekleyen **.got.plt** – yazılabilir kalır. * Bu nedenle, bir **keyfi yazma** ilkesinin hala bir PLT girişini yazması (veya **ret2dlresolve** gerçekleştirmesi) mümkündür. * Performans etkisi önemsizdir ve bu nedenle **neredeyse her dağıtım yıllardır en az Kısmi RELRO ile paketler gönderiyor (2016 itibarıyla GCC/Binutils varsayılanıdır)**. ### Tam RELRO * **Her iki** bayrak ile üretilir: `-Wl,-z,relro,-z,now` (diğer adıyla `-z relro -z now`). `-z now`, dinamik yükleyicinin **tüm** sembolleri önceden çözmesini (istekli bağlama) zorlar, böylece **.got.plt** bir daha yazılmak zorunda kalmaz ve güvenli bir şekilde yalnızca okunur olarak haritalanabilir. * Tüm **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** ve birkaç ek iç glibc tablosu yalnızca okunur bir `PT_GNU_RELRO` segmentinin içine yerleştirilir. * Ölçülebilir bir başlangıç yükü ekler (tüm dinamik yeniden konumlandırmalar başlatıldığında işlenir) ancak **çalışma zamanı yükü yoktur**. 2023'ten itibaren birkaç ana akım dağıtım, **sistem araç zincirini** (ve çoğu paketi) varsayılan olarak **Tam RELRO ile derlemeye** geçti – örneğin, **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** ve **Fedora 35+**. Bu nedenle, bir pentester olarak **her GOT girişinin yalnızca okunur** olduğu ikili dosyalarla karşılaşmayı beklemelisiniz. --- ## Bir ikilinin RELRO durumunu nasıl kontrol edersiniz ```bash $ checksec --file ./vuln [*] '/tmp/vuln' Arch: amd64-64-little RELRO: Full Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) ``` `checksec` (bir parçası [pwntools](https://github.com/pwncollege/pwntools) ve birçok dağıtım) `ELF` başlıklarını ayrıştırır ve koruma seviyesini yazdırır. Eğer `checksec` kullanamıyorsanız, `readelf`'e güvenin: ```bash # Partial RELRO → PT_GNU_RELRO is present but BIND_NOW is *absent* $ readelf -l ./vuln | grep -E "GNU_RELRO|BIND_NOW" GNU_RELRO 0x0000000000600e20 0x0000000000600e20 ``` ```bash # Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag $ readelf -d ./vuln | grep BIND_NOW 0x0000000000000010 (FLAGS) FLAGS: BIND_NOW ``` Eğer ikili çalışıyorsa (örneğin, bir set-uid root yardımcı programı), yürütülebilir dosyayı **`/proc/$PID/exe`** üzerinden inceleyebilirsiniz: ```bash readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO ``` --- ## Kendi kodunuzu derlerken RELRO'yu etkinleştirme ```bash # GCC example – create a PIE with Full RELRO and other common hardenings $ gcc -fPIE -pie -z relro -z now -Wl,--as-needed -D_FORTIFY_SOURCE=2 main.c -o secure ``` `-z relro -z now` hem **GCC/clang** ( `-Wl,` sonrasında geçildiğinde) hem de doğrudan **ld** için çalışır. **CMake 3.18+** kullanırken, yerleşik ön ayar ile Tam RELRO talep edebilirsiniz: ```cmake set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) # LTO set(CMAKE_ENABLE_EXPORTS OFF) set(CMAKE_BUILD_RPATH_USE_ORIGIN ON) set(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now") ``` --- ## Bypass Teknikleri | RELRO seviyesi | Tipik ilkel | Olası istismar teknikleri | |----------------|-------------|---------------------------| | Yok / Kısmi | Keyfi yazma | 1. **.got.plt** girişini üzerine yaz ve yürütmeyi yönlendir.
2. **ret2dlresolve** – yazılabilir bir segmentte sahte `Elf64_Rela` & `Elf64_Sym` oluştur ve `_dl_runtime_resolve` çağrısı yap.
3. **.fini_array** / **atexit()** listesinde fonksiyon işaretçilerini üzerine yaz. | | Tam | GOT salt okunur | 1. **diğer yazılabilir kod işaretçilerini** (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, özel `.data` bölümlerindeki geri çağırmalar, JIT sayfaları) ara.
2. libc'yi sızdırmak ve **SROP/ROP into libc** gerçekleştirmek için *göreceli okuma* ilkelerini kötüye kullan.
3. **DT_RPATH**/`LD_PRELOAD` aracılığıyla bir sahte paylaşılan nesne enjekte et (eğer ortam saldırganın kontrolündeyse) veya **`ld_audit`**.
4. GOT'a dokunmadan kontrol akışını saptırmak için **format-string** veya kısmi işaretçi üzerine yazma istismar et. | > 💡 Tam RELRO ile bile, **yüklenen paylaşılan kütüphanelerin GOT'u (örneğin libc'nin kendisi)** **yalnızca Kısmi RELRO**'dur çünkü bu nesneler yükleyici yerleştirmeleri uyguladığında zaten haritalanmıştır. Eğer başka bir paylaşılan nesnenin sayfalarını hedef alabilen bir **keyfi yazma** ilkesine sahip olursanız, libc'nin GOT girişlerini veya `__rtld_global` yığınını üzerine yazarak yürütmeyi yönlendirebilirsiniz; bu, modern CTF zorluklarında düzenli olarak istismar edilen bir tekniktir. ### Gerçek dünya bypass örneği (2024 CTF – *pwn.college “aydınlanmış”*) Zorluk Tam RELRO ile gönderildi. İstismar, bir yığın parçasının boyutunu bozmak için bir **off-by-one** kullandı, `tcache poisoning` ile libc'yi sızdırdı ve sonunda `__free_hook`'u (RELRO segmentinin dışında) bir gadget ile kod yürütmek için üzerine yazdı. Hiçbir GOT yazma gerekmiyordu. --- ## Son araştırmalar & güvenlik açıkları (2022-2025) * **glibc 2.40 `__malloc_hook` / `__free_hook`'u kullanımdan kaldırıyor (2025)** – Bu sembolleri kötüye kullanan modern yığın istismarlarının çoğu artık **`rtld_global._dl_load_jump`** veya C++ istisna tabloları gibi alternatif vektörlere geçmek zorundadır. Kancalar **RELRO'nun dışındadır**, bu nedenle kaldırılmaları Tam RELRO bypass'larını zorlaştırır. * **Binutils 2.41 “max-page-size” düzeltmesi (2024)** – Bir hata, RELRO segmentinin son birkaç baytının bazı ARM64 derlemelerinde yazılabilir verilerle aynı sayfayı paylaşmasına izin verdi ve `mprotect`'ten sonra yazılabilecek küçük bir **RELRO boşluğu** bıraktı. Üst akım artık `PT_GNU_RELRO`'yu sayfa sınırlarına hizalıyor ve bu kenar durumunu ortadan kaldırıyor. --- ## Referanslar * Binutils belgeleri – *`-z relro`, `-z now` ve `PT_GNU_RELRO`* * *“RELRO – Tam, Kısmi ve Bypass Teknikleri”* – blog yazısı @ wolfslittlered 2023 {{#include ../../banners/hacktricks-training.md}}