Translated ['src/binary-exploitation/common-binary-protections-and-bypas

This commit is contained in:
Translator 2025-07-17 00:13:16 +00:00
parent d1d8b66fd4
commit c95c522374

View File

@ -4,30 +4,96 @@
## Relro
**RELRO** označava **Relocation Read-Only**, i to je bezbednosna funkcija koja se koristi u binarnim datotekama kako bi se smanjili rizici povezani sa **GOT (Global Offset Table)** prepisivanjima. Postoje dve vrste **RELRO** zaštita: (1) **Delimični RELRO** i (2) **Potpuni RELRO**. Obe preuređuju **GOT** i **BSS** iz ELF datoteka, ali sa različitim rezultatima i implikacijama. Konkretno, postavljaju **GOT** sekciju _pre_ **BSS**. To jest, **GOT** je na nižim adresama od **BSS**, što onemogućava prepisivanje **GOT** unosa prelivanjem promenljivih u **BSS** (zapamtite da se pisanje u memoriju dešava od nižih ka višim adresama).
**RELRO** označava **Relocation Read-Only** i to je mitigacija koju implementira linker (`ld`) koja pretvara podskup ELF-ovih segmenata podataka u **samo za čitanje nakon što su sve relokacije primenjene**. Cilj je sprečiti napadača da prepisuje unose u **GOT (Global Offset Table)** ili druge tabele povezane sa relokacijom koje se dereferenciraju tokom izvršavanja programa (npr. `__fini_array`).
Hajde da razložimo koncept na njegove dve različite vrste radi jasnoće.
Moderni linkeri implementiraju RELRO tako što **preuređuju** **GOT** (i nekoliko drugih sekcija) tako da se nalaze **pre** **.bss** i što je najvažnije kreiraju posvećen `PT_GNU_RELRO` segment koji se premapira `RX` odmah nakon što dinamički učitač završi sa primenom relokacija. Kao rezultat, tipični preljevi bafera u **.bss** više ne mogu da dođu do GOT-a i primitivne operacije za proizvoljno pisanje ne mogu se koristiti za prepisivanje pokazivača funkcija koji se nalaze unutar stranice zaštićene RELRO-om.
### **Delimični RELRO**
Postoje **dva nivoa** zaštite koje linker može da generiše:
**Delimični RELRO** koristi jednostavniji pristup za poboljšanje bezbednosti bez značajnog uticaja na performanse binarne datoteke. Delimični RELRO čini **.got samo za čitanje (ne-PLT deo GOT sekcije)**. Imajte na umu da je ostatak sekcije (kao što je .got.plt) i dalje moguće pisati i, stoga, podložan napadima. Ovo **ne sprečava GOT** da bude zloupotrebljen **iz ranjivosti slobodnog pisanja**.
### Partial RELRO
Napomena: Po defaultu, GCC kompajlira binarne datoteke sa Delimičnim RELRO.
* Proizvedeno sa flagom `-Wl,-z,relro` (ili samo `-z relro` kada se direktno poziva `ld`).
* Samo **ne-PLT** deo **GOT**-a (deo koji se koristi za relokacije podataka) se stavlja u segment samo za čitanje. Sekcije koje treba da se modifikuju u vreme izvršavanja najvažnije **.got.plt** koja podržava **lazy binding** ostaju zapisive.
* Zbog toga, **primitivna operacija za proizvoljno pisanje** još uvek može preusmeriti tok izvršavanja prepisivanjem PLT unosa (ili izvođenjem **ret2dlresolve**).
* Uticaj na performanse je zanemarljiv i stoga **skoro svaka distribucija već godinama isporučuje pakete sa barem Partial RELRO (to je podrazumevano u GCC/Binutils od 2016)**.
### **Potpuni RELRO**
### Full RELRO
**Potpuni RELRO** pojačava zaštitu tako što **čini celu GOT (i .got i .got.plt) i .fini_array** sekciju potpuno **samo za čitanje.** Kada se binarna datoteka pokrene, svi adrese funkcija se rešavaju i učitavaju u GOT, zatim, GOT se označava kao samo za čitanje, efikasno sprečavajući bilo kakve izmene tokom izvršavanja.
* Proizvedeno sa **oba** flaga `-Wl,-z,relro,-z,now` (poznato i kao `-z relro -z now`). `-z now` prisiljava dinamički učitač da reši **sve** simbole unapred (eager binding) tako da **.got.plt** nikada ne mora ponovo da se piše i može sigurno da se mapira kao samo za čitanje.
* Ceo **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** i nekoliko dodatnih internih glibc tabela završavaju unutar segmenta samo za čitanje `PT_GNU_RELRO`.
* Dodaje merljiv početni prekorak (sve dinamičke relokacije se obrađuju pri pokretanju) ali **nema prekoraka u vreme izvršavanja**.
Međutim, kompromis sa Potpunim RELRO je u pogledu performansi i vremena pokretanja. Pošto je potrebno da se reše svi dinamički simboli prilikom pokretanja pre nego što se GOT označi kao samo za čitanje, **binarne datoteke sa omogućеним Potpunim RELRO mogu doživeti duže vreme učitavanja**. Ova dodatna prekomerna opterećenja prilikom pokretanja su razlog zašto Potpuni RELRO nije omogućен po defaultu u svim binarnim datotekama.
Od 2023. godine nekoliko mainstream distribucija prešlo je na kompajliranje **sistemskog alata** (i većine paketa) sa **Full RELRO podrazumevano** npr. **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** i **Fedora 35+**. Kao pentester, stoga treba da očekujete da se susretnete sa binarnim datotekama gde je **svaki GOT unos samo za čitanje**.
Moguće je videti da li je Potpuni RELRO **omogućen** u binarnoj datoteci sa:
---
## Kako proveriti RELRO status binarne datoteke
```bash
readelf -l /proc/ID_PROC/exe | grep BIND_NOW
$ checksec --file ./vuln
[*] '/tmp/vuln'
Arch: amd64-64-little
RELRO: Full
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
```
`checksec` (deo [pwntools](https://github.com/pwncollege/pwntools) i mnogih distribucija) analizira `ELF` zaglavlja i prikazuje nivo zaštite. Ako ne možete koristiti `checksec`, oslonite se na `readelf`:
```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
```
## Bypass
Ako je Full RELRO omogućen, jedini način da se zaobiđe je da se pronađe drugi način koji ne zahteva pisanje u GOT tabelu za dobijanje proizvoljne izvršne radnje.
```bash
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
```
Ako se binarni program izvršava (npr. set-uid root pomoćnik), još uvek možete pregledati izvršni fajl **putem `/proc/$PID/exe`**:
```bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
```
---
Napomena da je **LIBC-ova GOT obično Partial RELRO**, tako da se može modifikovati sa proizvoljnim pisanjem. Više informacija u [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
## Omogućavanje RELRO prilikom kompajliranja vlastitog koda
```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` ради за оба **GCC/clang** (prolazi nakon `-Wl,`) i **ld** direktno. Kada koristite **CMake 3.18+** možete zatražiti Full RELRO sa ugrađenim presetom:
```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")
```
---
## Tehnike zaobilaženja
| RELRO nivo | Tipična primitivna | Moguće tehnike eksploatacije |
|-------------|-------------------|----------------------------------|
| Nema / Delimična | Arbitrarna pisanja | 1. Prepisivanje **.got.plt** unosa i preusmeravanje izvršenja.<br>2. **ret2dlresolve** kreirati lažni `Elf64_Rela` & `Elf64_Sym` u segmentu koji se može pisati i pozvati `_dl_runtime_resolve`.<br>3. Prepisivanje pokazivača funkcija u **.fini_array** / **atexit()** listi. |
| Potpuna | GOT je samo za čitanje | 1. Potražiti **druge pokazivače na kod koji se može pisati** (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, povratni pozivi u prilagođenim `.data` sekcijama, JIT stranice).<br>2. Zloupotreba *relativnih čitanja* primitiva za curenje libc i izvođenje **SROP/ROP u libc**.<br>3. Umetanje zlonamerne deljene biblioteke putem **DT_RPATH**/`LD_PRELOAD` (ako je okruženje pod kontrolom napadača) ili **`ld_audit`**.<br>4. Eksploatacija **format-string** ili delimično prepisivanje pokazivača za preusmeravanje toka kontrole bez dodirivanja GOT-a. |
> 💡 Čak i sa Potpunim RELRO, **GOT učitanih deljenih biblioteka (npr. libc sama)** je **samo Delimični RELRO** jer su ti objekti već mapirani kada učitavač primeni relokacije. Ako dobijete **arbitrarnu pisanje** primitivu koja može ciljati stranice druge deljene biblioteke, još uvek možete preusmeriti izvršenje prepisivanjem libc-ovih GOT unosa ili `__rtld_global` steka, tehnika koja se redovno koristi u modernim CTF izazovima.
### Primer zaobilaženja iz stvarnog sveta (2024 CTF *pwn.college “enlightened”*)
Izazov je isporučen sa Potpunim RELRO. Eksploit je koristio **off-by-one** za korupciju veličine heap chunk-a, iscurio libc sa `tcache poisoning`, i konačno prepisao `__free_hook` (van RELRO segmenta) sa jednim gadgetom za dobijanje izvršenja koda. Nije bilo potrebno pisanje u GOT.
---
## Nedavna istraživanja i ranjivosti (2022-2025)
* **glibc 2.40 deprecira `__malloc_hook` / `__free_hook` (2025)** Većina modernih heap eksploata koji su zloupotrebljavali ove simbole sada mora preći na alternativne vektore kao što su **`rtld_global._dl_load_jump`** ili C++ tabele izuzetaka. Zbog toga što hook-ovi žive **van** RELRO, njihovo uklanjanje povećava težinu zaobilaženja Potpunog RELRO-a.
* **Binutils 2.41 “max-page-size” ispravka (2024)** Greška je omogućila poslednjih nekoliko bajtova RELRO segmenta da dele stranicu sa podacima koji se mogu pisati na nekim ARM64 verzijama, ostavljajući mali **RELRO razmak** koji se mogao pisati nakon `mprotect`. Upstream sada usklađuje `PT_GNU_RELRO` sa granicama stranica, eliminišući taj ivični slučaj.
---
## Reference
* Dokumentacija za Binutils *`-z relro`, `-z now` i `PT_GNU_RELRO`*
* *“RELRO Potpuni, Delimični i Tehnike zaobilaženja”* blog post @ wolfslittlered 2023
{{#include ../../banners/hacktricks-training.md}}