7.5 KiB
Raw Blame History

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 RX 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

$ 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:

# 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
# 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:

readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO

Kendi kodunuzu derlerken RELRO'yu etkinleştirme

# 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:

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}}