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

This commit is contained in:
Translator 2025-07-17 00:13:30 +00:00
parent 5b4f3bb6da
commit d74f89255c

View File

@ -4,30 +4,96 @@
## Relro
**RELRO** staan vir **Relocation Read-Only**, en dit is 'n sekuriteitskenmerk wat in binêre lêers gebruik word om die risiko's wat verband hou met **GOT (Global Offset Table)** oorskrywings te verminder. Daar is twee tipes **RELRO** beskermings: (1) **Deeltelike RELRO** en (2) **Volledige RELRO**. Albei herskik die **GOT** en **BSS** van ELF-lêers, maar met verskillende resultate en implikasies. Spesifiek plaas hulle die **GOT** afdeling _voor_ die **BSS**. Dit wil sê, **GOT** is op laer adresse as **BSS**, wat dit onmoontlik maak om **GOT** inskrywings te oorskry deur oorloop van veranderlikes in die **BSS** (onthou dat skryf in geheue van laer na hoër adresse gebeur).
**RELRO** staan vir **Relocation Read-Only** en dit is 'n versagting wat deur die linker (`ld`) geïmplementeer word wat 'n substel van die ELF se datasegmente **lees-alleen maak nadat alle herlokasies toegepas is**. Die doel is om 'n aanvaller te stop om inskrywings in die **GOT (Global Offset Table)** of ander herlokasie-verwante tabelle wat tydens programuitvoering gedereferensieer word (bv. `__fini_array`) te oorskry.
Kom ons breek die konsep af in sy twee duidelike tipes vir duidelikheid.
Moderne linkers implementeer RELRO deur die **GOT** (en 'n paar ander afdelings) te **herorden** sodat hulle **voor** die **.bss** woon en die belangrikste deur 'n toegewyde `PT_GNU_RELRO` segment te skep wat `RX` herkaarteer reg na die dinamiese laaier klaar is met die toepassing van herlokasies. Gevolglik kan tipiese buffer oorgroeis in die **.bss** nie meer die GOT bereik nie en arbitrêre skryfprimitiewe kan nie gebruik word om funksie-aanwysers wat binne 'n RELRO-beskermde bladsy sit, te oorskry nie.
### **Deeltelike RELRO**
Daar is **twee vlakke** van beskerming wat die linker kan uitreik:
**Deeltelike RELRO** neem 'n eenvoudiger benadering om sekuriteit te verbeter sonder om die binêre se prestasie aansienlik te beïnvloed. Deeltelike RELRO maak **die .got slegs leesbaar (die nie-PLT deel van die GOT afdeling)**. Hou in gedagte dat die res van die afdeling (soos die .got.plt) steeds skryfbaar is en, gevolglik, onderhewig is aan aanvalle. Dit **verhoed nie dat die GOT** misbruik word **van arbitrêre skryf** kwesbaarhede nie.
### Gedeeltelike RELRO
Nota: Standaard kompileer GCC binêre lêers met Deeltelike RELRO.
* Geproduseer met die vlag `-Wl,-z,relro` (of net `-z relro` wanneer `ld` direk aangeroep word).
* Slegs die **nie-PLT** deel van die **GOT** (die deel wat vir dataskakelings gebruik word) word in die lees-alleen segment geplaas. Afdelings wat tydens uitvoering gewysig moet word die belangrikste is **.got.plt** wat **luie binding** ondersteun bly skryfbaar.
* As gevolg hiervan kan 'n **arbitrêre skryf** primitiewe steeds die uitvoeringsvloei herlei deur 'n PLT-inskrywing te oorskry (of deur **ret2dlresolve** uit te voer).
* Die prestasie-impak is verwaarloosbaar en daarom **het byna elke verspreiding al jare lank pakkette met ten minste Gedeeltelike RELRO gestuur (dit is die GCC/Binutils standaard sedert 2016)**.
### **Volledige RELRO**
### Volledige RELRO
**Volledige RELRO** verhoog die beskerming deur **die hele GOT (both .got en .got.plt) en .fini_array** afdeling heeltemal **slegs leesbaar** te maak. Sodra die binêre begin, word al die funksie adresse opgelos en in die GOT gelaai, dan word GOT as slegs leesbaar gemerk, wat effektief enige wysigings aan dit tydens uitvoering voorkom.
* Geproduseer met **albei** vlae `-Wl,-z,relro,-z,now` (ook bekend as `-z relro -z now`). `-z now` dwing die dinamiese laaier om **alle** simbole vooraf op te los (gretige binding) sodat **.got.plt** nooit weer geskryf hoef te word nie en veilig lees-alleen kan wees.
* Die hele **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** en 'n paar addisionele interne glibc tabelle eindig binne 'n lees-alleen `PT_GNU_RELRO` segment.
* Voeg meetbare opstart oorhoofse koste by (alle dinamiese herlokasies word by die bekendstelling verwerk) maar **geen uitvoering oorhoofse koste** nie.
Die handelsoffensief met Volledige RELRO is egter in terme van prestasie en opstarttyd. Omdat dit al die dinamiese simbole by opstart moet oplos voordat dit die GOT as slegs leesbaar merk, **kan binêre lêers met Volledige RELRO geaktiveer langer laai tye ervaar**. Hierdie ekstra opstartlas is waarom Volledige RELRO nie standaard in alle binêre lêers geaktiveer is nie.
Sedert 2023 het verskeie hoofstroom verspreidings oorgeskakel na die kompilering van die **stelsels hulpmiddel-ketting** (en die meeste pakkette) met **Volledige RELRO as standaard** bv. **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** en **Fedora 35+**. As 'n pentester moet jy dus verwag om binaries te teëkom waar **elke GOT-inskrywing lees-alleen is**.
Dit is moontlik om te sien of Volledige RELRO **geaktiveer** is in 'n binêre met:
---
## Hoe om die RELRO-status van 'n binêre te kontroleer
```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` (deel van [pwntools](https://github.com/pwncollege/pwntools) en baie verspreidings) ontleed `ELF` koppe en druk die beskermingsvlak. As jy nie `checksec` kan gebruik nie, vertrou op `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
As Full RELRO geaktiveer is, is die enigste manier om dit te omseil om 'n ander manier te vind wat nie nodig is om in die GOT tabel te skryf om arbitrêre uitvoering te verkry nie.
```bash
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
```
As die binêre loop (bv. 'n set-uid root helper), kan jy steeds die uitvoerbare lêer inspekteer **via `/proc/$PID/exe`**:
```bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
```
---
Let daarop dat **LIBC se GOT gewoonlik Partial RELRO is**, so dit kan met 'n arbitrêre skrywe gewysig word. Meer inligting in [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
## Aktivering van RELRO wanneer jy jou eie kode saamstel
```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` werk vir beide **GCC/clang** (gegee na `-Wl,`) en **ld** direk. Wanneer jy **CMake 3.18+** gebruik, kan jy Volle RELRO met die ingeboude voorinstelling versoek:
```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 Tegnieke
| RELRO vlak | Tipiese primitiewe | Moglike uitbuitingstegnieke |
|-------------|-------------------|----------------------------------|
| Geen / Gedeeltelik | Willekeurige skrywe | 1. Oorskry **.got.plt** inskrywing en draai uitvoering om.<br>2. **ret2dlresolve** vervaardig vals `Elf64_Rela` & `Elf64_Sym` in 'n skryfbare segment en bel `_dl_runtime_resolve` aan.<br>3. Oorskry funksie-aanwysers in **.fini_array** / **atexit()** lys. |
| Vol | GOT is lees-slegs | 1. Soek **ander skryfbare kode-aanwysers** (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, terugroepe in pasgemaakte `.data` afdelings, JIT bladsye).<br>2. Misbruik *relatiewe lees* primitiewe om libc te lek en **SROP/ROP in libc** uit te voer.<br>3. Spuit 'n rogue gedeelde objek via **DT_RPATH**/`LD_PRELOAD` (as omgewing deur die aanvaller beheer word) of **`ld_audit`**.<br>4. Exploit **formaat-string** of gedeeltelike aanwyser oorskrywing om die beheerstroom te lei sonder om die GOT aan te raak. |
> 💡 Selfs met Vol RELRO is die **GOT van gelaaide gedeelde biblioteke (bv. libc self)** **slegs Gedeeltelik RELRO** omdat daardie objekte reeds gemap is wanneer die laaier herlokasies toepas. As jy 'n **willekeurige skrywe** primitiewe verkry wat 'n ander gedeelde objek se bladsye kan teiken, kan jy steeds uitvoering draai deur libc se GOT inskrywings of die `__rtld_global` stapel oor te skryf, 'n tegniek wat gereeld in moderne CTF-uitdagings benut word.
### Werklike wêreld omseil voorbeeld (2024 CTF *pwn.college “verlig”*)
Die uitdaging is gestuur met Vol RELRO. Die uitbuiting het 'n **off-by-one** gebruik om die grootte van 'n heap stuk te korrupteer, libc gelekt met `tcache poisoning`, en uiteindelik `__free_hook` (buite die RELRO segment) oorgeskryf met 'n een-gadget om kode-uitvoering te verkry. Geen GOT skrywe was nodig nie.
---
## Onlangse navorsing & kwesbaarhede (2022-2025)
* **glibc 2.40 de-precates `__malloc_hook` / `__free_hook` (2025)** Meeste moderne heap uitbuitings wat hierdie simbole misbruik het nou die noodsaak om na alternatiewe vektore soos **`rtld_global._dl_load_jump`** of C++ uitsonderingstabelle te draai. Omdat haken **buite** RELRO leef, verhoog hul verwydering die moeilikheidsgraad van Vol-RELRO omseilings.
* **Binutils 2.41 “max-page-size” regstelling (2024)** 'n Fout het toegelaat dat die laaste paar bytes van die RELRO segment 'n bladsy met skryfbare data op sommige ARM64 boude deel, wat 'n klein **RELRO gaping** gelaat het wat na `mprotect` geskryf kon word. Upstream belyn nou `PT_GNU_RELRO` met bladsygrense, wat daardie randgeval uitskakel.
---
## Verwysings
* Binutils dokumentasie *`-z relro`, `-z now` en `PT_GNU_RELRO`*
* *“RELRO Vol, Gedeeltelik en Omseil Tegnieke”* blogpos @ wolfslittlered 2023
{{#include ../../banners/hacktricks-training.md}}