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

This commit is contained in:
Translator 2025-07-17 00:13:11 +00:00
parent 0e312beacd
commit a854d4271e

View File

@ -4,30 +4,96 @@
## Relro
**RELRO** sta per **Relocation Read-Only** ed è una funzione di sicurezza utilizzata nei binari per mitigare i rischi associati con le sovrascritture della **GOT (Global Offset Table)**. Ci sono due tipi di protezioni **RELRO**: (1) **Partial RELRO** e (2) **Full RELRO**. Entrambi riordinano la **GOT** e la **BSS** dai file ELF, ma con risultati e implicazioni diverse. Specificamente, pongono la sezione **GOT** _prima_ della **BSS**. Cioè, **GOT** si trova a indirizzi più bassi rispetto alla **BSS**, rendendo quindi impossibile sovrascrivere le voci della **GOT** sovrascrivendo le variabili nella **BSS** (ricorda che la scrittura in memoria avviene da indirizzi più bassi verso indirizzi più alti).
**RELRO** sta per **Relocation Read-Only** ed è una mitigazione implementata dal linker (`ld`) che rende un sottoinsieme dei segmenti di dati ELF **sola lettura dopo che tutte le relocazioni sono state applicate**. L'obiettivo è fermare un attaccante dall'overwriting delle voci nella **GOT (Global Offset Table)** o in altre tabelle relative alle relocazioni che vengono dereferenziate durante l'esecuzione del programma (ad es. `__fini_array`).
Analizziamo il concetto nei suoi due tipi distinti per chiarezza.
I linker moderni implementano RELRO **riordinando** la **GOT** (e alcune altre sezioni) in modo che vivano **prima** della **.bss** e cosa più importante creando un segmento dedicato `PT_GNU_RELRO` che viene rimappato `RX` subito dopo che il caricatore dinamico ha finito di applicare le relocazioni. Di conseguenza, i tipici buffer overflow nella **.bss** non possono più raggiungere la GOT e le primitive di scrittura arbitraria non possono essere utilizzate per sovrascrivere i puntatori di funzione che si trovano all'interno di una pagina protetta da RELRO.
### **Partial RELRO**
Ci sono **due livelli** di protezione che il linker può emettere:
**Partial RELRO** adotta un approccio più semplice per migliorare la sicurezza senza influire significativamente sulle prestazioni del binario. Partial RELRO rende **la .got di sola lettura (la parte non-PLT della sezione GOT)**. Tieni presente che il resto della sezione (come la .got.plt) è ancora scrivibile e, quindi, soggetto ad attacchi. Questo **non impedisce alla GOT** di essere abusata **da vulnerabilità di scrittura arbitraria**.
### Partial RELRO
Nota: Per impostazione predefinita, GCC compila i binari con Partial RELRO.
* Prodotto con il flag `-Wl,-z,relro` (o semplicemente `-z relro` quando si invoca `ld` direttamente).
* Solo la parte **non-PLT** della **GOT** (la parte utilizzata per le relocazioni dei dati) viene messa nel segmento di sola lettura. Le sezioni che devono essere modificate a runtime in particolare **.got.plt** che supporta il **lazy binding** rimangono scrivibili.
* Per questo motivo, una primitive di **scrittura arbitraria** può ancora reindirizzare il flusso di esecuzione sovrascrivendo un'entrata PLT (o eseguendo **ret2dlresolve**).
* L'impatto sulle prestazioni è trascurabile e quindi **quasi ogni distribuzione ha spedito pacchetti con almeno Partial RELRO per anni (è il default di GCC/Binutils dal 2016)**.
### **Full RELRO**
### Full RELRO
**Full RELRO** aumenta la protezione rendendo **l'intera GOT (sia .got che .got.plt) e la sezione .fini_array** completamente **di sola lettura.** Una volta che il binario inizia, tutti gli indirizzi delle funzioni vengono risolti e caricati nella GOT, quindi, la GOT viene contrassegnata come di sola lettura, prevenendo efficacemente qualsiasi modifica durante l'esecuzione.
* Prodotto con **entrambi** i flag `-Wl,-z,relro,-z,now` (alias `-z relro -z now`). `-z now` costringe il caricatore dinamico a risolvere **tutti** i simboli in anticipo (eager binding) in modo che **.got.plt** non debba mai essere riscritta e possa essere mappata in modo sicuro come sola lettura.
* L'intera **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** e alcune tabelle interne aggiuntive di glibc finiscono all'interno di un segmento `PT_GNU_RELRO` di sola lettura.
* Aggiunge un sovraccarico misurabile all'avvio (tutte le relocazioni dinamiche vengono elaborate all'avvio) ma **nessun sovraccarico a runtime**.
Tuttavia, il compromesso con Full RELRO è in termini di prestazioni e tempo di avvio. Poiché deve risolvere tutti i simboli dinamici all'avvio prima di contrassegnare la GOT come di sola lettura, **i binari con Full RELRO abilitato potrebbero sperimentare tempi di caricamento più lunghi**. Questo sovraccarico aggiuntivo all'avvio è il motivo per cui Full RELRO non è abilitato per impostazione predefinita in tutti i binari.
Dal 2023 diverse distribuzioni principali hanno iniziato a compilare la **tool-chain di sistema** (e la maggior parte dei pacchetti) con **Full RELRO per default** ad es. **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** e **Fedora 35+**. Come pentester, dovresti quindi aspettarti di incontrare binari in cui **ogni voce GOT è di sola lettura**.
È possibile vedere se Full RELRO è **abilitato** in un binario con:
---
## Come controllare lo stato RELRO di un binario
```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` (parte di [pwntools](https://github.com/pwncollege/pwntools) e molte distribuzioni) analizza gli header `ELF` e stampa il livello di protezione. Se non puoi usare `checksec`, fai affidamento su `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
Se il Full RELRO è abilitato, l'unico modo per bypassarlo è trovare un altro modo che non richieda di scrivere nella tabella GOT per ottenere un'esecuzione arbitraria.
```bash
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
```
Se il binario è in esecuzione (ad esempio, un helper set-uid root), puoi comunque ispezionare l'eseguibile **via `/proc/$PID/exe`**:
```bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
```
---
Nota che **il GOT di LIBC è solitamente Partial RELRO**, quindi può essere modificato con una scrittura arbitraria. Maggiori informazioni in [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
## Abilitare RELRO durante la compilazione del proprio codice
```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` funziona sia per **GCC/clang** (passato dopo `-Wl,`) che per **ld** direttamente. Quando si utilizza **CMake 3.18+** è possibile richiedere Full RELRO con il preset integrato:
```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")
```
---
## Tecniche di Bypass
| Livello RELRO | Primitiva tipica | Tecniche di sfruttamento possibili |
|---------------|------------------|-------------------------------------|
| Nessuno / Parziale | Scrittura arbitraria | 1. Sovrascrivere l'entrata di **.got.plt** e cambiare l'esecuzione.<br>2. **ret2dlresolve** creare un falso `Elf64_Rela` & `Elf64_Sym` in un segmento scrivibile e chiamare `_dl_runtime_resolve`.<br>3. Sovrascrivere puntatori di funzione in **.fini_array** / lista **atexit()**. |
| Completo | GOT è di sola lettura | 1. Cercare **altri puntatori di codice scrivibili** (vtables C++, `__malloc_hook` < glibc 2.34, `__free_hook`, callback in sezioni `.data` personalizzate, pagine JIT).<br>2. Abusare delle primitività *lettura relativa* per rivelare libc e eseguire **SROP/ROP in libc**.<br>3. Iniettare un oggetto condiviso malevolo tramite **DT_RPATH**/`LD_PRELOAD` (se l'ambiente è controllato dall'attaccante) o **`ld_audit`**.<br>4. Sfruttare **format-string** o sovrascrittura parziale di puntatori per deviare il flusso di controllo senza toccare il GOT. |
> 💡 Anche con Full RELRO il **GOT delle librerie condivise caricate (ad es. libc stessa)** è **solo RELRO Parziale** perché quegli oggetti sono già mappati quando il loader applica le rilocazioni. Se ottieni una primitiva di **scrittura arbitraria** che può mirare alle pagine di un altro oggetto condiviso, puoi comunque cambiare l'esecuzione sovrascrivendo le entrate del GOT di libc o lo stack di `__rtld_global`, una tecnica regolarmente sfruttata nelle moderne sfide CTF.
### Esempio di bypass nel mondo reale (2024 CTF *pwn.college “illuminato”*)
La sfida è stata fornita con Full RELRO. Lo sfruttamento ha utilizzato un **off-by-one** per corrompere la dimensione di un chunk di heap, ha rivelato libc con `tcache poisoning`, e infine ha sovrascritto `__free_hook` (fuori dal segmento RELRO) con un one-gadget per ottenere l'esecuzione di codice. Non è stata necessaria alcuna scrittura nel GOT.
---
## Ricerche recenti & vulnerabilità (2022-2025)
* **glibc 2.40 deprecates `__malloc_hook` / `__free_hook` (2025)** La maggior parte degli exploit moderni dell'heap che abusavano di questi simboli devono ora passare a vettori alternativi come **`rtld_global._dl_load_jump`** o tabelle di eccezione C++. Poiché i ganci vivono **fuori** dal RELRO, la loro rimozione aumenta la difficoltà dei bypass Full-RELRO.
* **Fix “max-page-size” di Binutils 2.41 (2024)** Un bug consentiva agli ultimi byte del segmento RELRO di condividere una pagina con dati scrivibili su alcune build ARM64, lasciando un piccolo **gap RELRO** che poteva essere scritto dopo `mprotect`. L'upstream ora allinea `PT_GNU_RELRO` ai confini delle pagine, eliminando quel caso limite.
---
## Riferimenti
* Documentazione di Binutils *`-z relro`, `-z now` e `PT_GNU_RELRO`*
* *“RELRO Tecniche Complete, Parziali e di Bypass”* post del blog @ wolfslittlered 2023
{{#include ../../banners/hacktricks-training.md}}