100 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```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.<br>2. **ret2dlresolve** yazılabilir bir segmentte sahte `Elf64_Rela` & `Elf64_Sym` oluştur ve `_dl_runtime_resolve` çağrısı yap.<br>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.<br>2. libc'yi sızdırmak ve **SROP/ROP into libc** gerçekleştirmek için *göreceli okuma* ilkelerini kötüye kullan.<br>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`**.<br>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}}