Relro

Reading time: 6 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

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 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 seviyesiTipik ilkelOlası istismar teknikleri
Yok / KısmiKeyfi yazma1. .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.
TamGOT salt okunur1. 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

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking'i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin