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

This commit is contained in:
Translator 2025-07-17 00:13:23 +00:00
parent 143b7f9b26
commit dfb26b32d9

View File

@ -4,30 +4,96 @@
## Relro
**RELRO** inamaanisha **Relocation Read-Only**, na ni kipengele cha usalama kinachotumika katika binaries kupunguza hatari zinazohusiana na **GOT (Global Offset Table)** overwrites. Kuna aina mbili za ulinzi wa **RELRO**: (1) **Partial RELRO** na (2) **Full RELRO**. Zote zinapanga upya **GOT** na **BSS** kutoka kwa faili za ELF, lakini kwa matokeo na athari tofauti. Kwa hakika, zinaweka sehemu ya **GOT** _kabla_ ya **BSS**. Hiyo ni, **GOT** iko katika anwani za chini kuliko **BSS**, hivyo kufanya iwezekanavyo kuandika tena **GOT** entries kwa kujaa kwa mabadiliko katika **BSS** (kumbuka kuandika kwenye kumbukumbu kunatokea kutoka anwani za chini kuelekea juu).
**RELRO** inamaanisha **Relocation Read-Only** na ni hatua ya kupunguza hatari iliyotekelezwa na linker (`ld`) ambayo inafanya sehemu fulani za data za ELF kuwa **read-only baada ya mabadiliko yote kutumika**. Lengo ni kuzuia mshambuliaji kuandika upya entries katika **GOT (Global Offset Table)** au meza nyingine zinazohusiana na mabadiliko ambazo zinarejelea wakati wa utekelezaji wa programu (mfano `__fini_array`).
Hebu tufanye uchambuzi wa dhana hii katika aina zake mbili tofauti kwa uwazi.
Linker za kisasa zinafanya RELRO kwa **kupanga upya** **GOT** (na sehemu chache nyingine) ili ziwe **kabla** ya **.bss** na muhimu zaidi kwa kuunda sehemu maalum ya `PT_GNU_RELRO` ambayo inarudiwa `RX` mara tu loader ya dynamic inapoisha kutekeleza mabadiliko. Kwa hivyo, overflow za kawaida za buffer katika **.bss** haziwezi tena kufikia GOT na primitives za kuandika zisizo na mipaka cannot be used to overwrite function pointers ambazo ziko ndani ya ukurasa uliohifadhiwa na RELRO.
### **Partial RELRO**
Kuna **viwango viwili** vya ulinzi ambavyo linker inaweza kutoa:
**Partial RELRO** inachukua njia rahisi kuboresha usalama bila kuathiri sana utendaji wa binary. Partial RELRO inafanya **.got iwe ya kusoma tu (sehemu isiyo ya PLT ya sehemu ya GOT)**. Kumbuka kwamba sehemu nyingine (kama vile .got.plt) bado inaweza kuandikwa, na hivyo, inakuwa chini ya mashambulizi. Hii **haiwezi kuzuia GOT** kutumika vibaya **kutokana na udhaifu wa kuandika bila mpangilio**.
### Partial RELRO
Kumbuka: Kwa kawaida, GCC inakusanya binaries na Partial RELRO.
* Inazalishwa kwa bendera `-Wl,-z,relro` (au tu `-z relro` unapoitisha `ld` moja kwa moja).
* Sehemu tu ya **non-PLT** ya **GOT** (sehemu inayotumika kwa mabadiliko ya data) inawekwa katika sehemu isiyo na maandiko. Sehemu ambazo zinahitaji kubadilishwa wakati wa utekelezaji muhimu zaidi **.got.plt** ambayo inasaidia **lazy binding** zinabaki kuwa na uwezo wa kuandikwa.
* Kwa sababu hiyo, primitive ya **kuandika bila mipaka** bado inaweza kuelekeza mtiririko wa utekelezaji kwa kuandika upya PLT entry (au kwa kufanya **ret2dlresolve**).
* Athari za utendaji ni ndogo na kwa hivyo **karibu kila usambazaji umekuwa ukisafirisha pakiti zikiwa na angalau Partial RELRO kwa miaka (ni chaguo la msingi la GCC/Binutils tangu 2016)**.
### **Full RELRO**
### Full RELRO
**Full RELRO** inaongeza ulinzi kwa **kufanya GOT yote (sawa na .got na .got.plt) na sehemu ya .fini_array** kuwa kabisa **ya kusoma tu.** Mara binary inapoanza, anwani zote za kazi zinatatuliwa na kupakiwa katika GOT, kisha, GOT inakumbukwa kama ya kusoma tu, kwa ufanisi kuzuia mabadiliko yoyote kwake wakati wa utendaji.
* Inazalishwa kwa **bendera zote** `-Wl,-z,relro,-z,now` (pia inajulikana kama `-z relro -z now`). `-z now` inalazimisha loader ya dynamic kutatua **mifano yote** mapema (eager binding) ili **.got.plt** isihitaji kuandikwa tena na inaweza kuandikwa kwa usalama kama read-only.
* **GOT** nzima, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** na meza chache za ndani za glibc zinaishia ndani ya sehemu isiyo na maandiko ya `PT_GNU_RELRO`.
* Inaleta ongezeko la gharama za kuanzisha zinazoweza kupimwa (mabadiliko yote ya dynamic yanashughulikiwa wakati wa uzinduzi) lakini **hakuna gharama za wakati wa utekelezaji**.
Hata hivyo, gharama ya Full RELRO iko katika utendaji na muda wa kuanzisha. Kwa sababu inahitaji kutatua alama zote za dinamik wakati wa kuanzisha kabla ya kuashiria GOT kama ya kusoma tu, **binaries zenye Full RELRO iliyoanzishwa zinaweza kukumbana na muda mrefu wa kupakia**. Mzigo huu wa ziada wa kuanzisha ndio sababu Full RELRO haijaanzishwa kwa kawaida katika binaries zote.
Tangu mwaka 2023 usambazaji kadhaa maarufu umepita katika kuunda **system tool-chain** (na pakiti nyingi) kwa **Full RELRO kama chaguo la msingi** mfano **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** na **Fedora 35+**. Kama pentester unapaswa hivyo kutarajia kukutana na binaries ambapo **kila GOT entry ni read-only**.
Inawezekana kuona ikiwa Full RELRO ime **anzishwa** katika binary kwa:
---
## Jinsi ya Kuangalia hali ya RELRO ya binary
```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` (sehemu ya [pwntools](https://github.com/pwncollege/pwntools) na usambazaji mwingi) inachambua vichwa vya `ELF` na kuchapisha kiwango cha ulinzi. Ikiwa huwezi kutumia `checksec`, tegemea `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
Ikiwa Full RELRO imewezeshwa, njia pekee ya kuipita ni kutafuta njia nyingine ambayo haitahitaji kuandika kwenye jedwali la GOT ili kupata utekelezaji wa kiholela.
```bash
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
$ readelf -d ./vuln | grep BIND_NOW
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
```
Ikiwa binary inafanya kazi (kwa mfano, msaidizi wa set-uid root), bado unaweza kuchunguza executable **via `/proc/$PID/exe`**:
```bash
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
```
---
Kumbuka kwamba **GOT ya LIBC kwa kawaida ni Partial RELRO**, hivyo inaweza kubadilishwa kwa kuandika kiholela. Taarifa zaidi katika [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
## Kuwezesha RELRO unapokuwa unakusanya msimbo wako mwenyewe
```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` inafanya kazi kwa **GCC/clang** (iliyopita baada ya `-Wl,`) na **ld** moja kwa moja. Wakati wa kutumia **CMake 3.18+** unaweza kuomba Full RELRO kwa preset iliyojengwa ndani:
```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")
```
---
## Mbinu za Kuepuka
| Kiwango cha RELRO | Kawaida primitive | Mbinu zinazowezekana za unyakuzi |
|-------------------|-------------------|----------------------------------|
| Hakuna / Sehemu | Kuandika bila mpangilio | 1. Badilisha **.got.plt** kuingia na pivot execution.<br>2. **ret2dlresolve** tengeneza `Elf64_Rela` & `Elf64_Sym` bandia katika sehemu inayoweza kuandikwa na kuita `_dl_runtime_resolve`.<br>3. Badilisha viashiria vya kazi katika orodha ya **.fini_array** / **atexit()**. |
| Kamili | GOT ni ya kusoma tu | 1. Tafuta **viashiria vingine vya kazi vinavyoweza kuandikwa** (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, callbacks katika sehemu za desturi `.data`, kurasa za JIT).<br>2. Tumia *relative read* primitives kuvuja libc na kufanya **SROP/ROP ndani ya libc**.<br>3. Ingiza kitu cha pamoja kisicho halali kupitia **DT_RPATH**/`LD_PRELOAD` (ikiwa mazingira yanadhibitiwa na mshambuliaji) au **`ld_audit`**.<br>4. Fanya unyakuzi wa **format-string** au kuandika sehemu ya kiashiria ili kuelekeza mtiririko wa udhibiti bila kugusa GOT. |
> 💡 Hata na Full RELRO, **GOT ya maktaba za pamoja zilizoloadiwa (k.m. libc yenyewe)** ni **sehemu tu ya RELRO** kwa sababu vitu hivyo tayari vimepangwa wakati mzigo unapotumia uhamasishaji. Ikiwa unapata primitive ya **kuandika bila mpangilio** inayoweza kulenga kurasa za kitu kingine cha pamoja, bado unaweza pivot execution kwa kubadilisha entries za GOT za libc au stack ya `__rtld_global`, mbinu inayotumiwa mara kwa mara katika changamoto za kisasa za CTF.
### Mfano wa kuepuka wa kweli (2024 CTF *pwn.college “enlightened”*)
Changamoto ililetwa na Full RELRO. Unyakuzi ulitumia **off-by-one** kuharibu ukubwa wa kipande cha heap, ukavuja libc kwa `tcache poisoning`, na hatimaye ukabadili `__free_hook` (nje ya sehemu ya RELRO) kwa gadget moja ili kupata utekelezaji wa msimbo. Hakuna kuandika GOT kulihitajika.
---
## Utafiti wa hivi karibuni & udhaifu (2022-2025)
* **glibc 2.40 inafuta `__malloc_hook` / `__free_hook` (2025)** Unyakuzi wa kisasa wa heap ambao ulitumia alama hizi sasa lazima uhamie kwenye njia mbadala kama **`rtld_global._dl_load_jump`** au meza za kutengwa za C++. Kwa sababu hooks ziko **nje** ya RELRO, kuondolewa kwao kunafanya iwe vigumu zaidi kuepuka Full-RELRO.
* **Binutils 2.41 “max-page-size” fix (2024)** Kosa liliruhusu bytes chache za mwisho za sehemu ya RELRO kushiriki ukurasa na data inayoweza kuandikwa kwenye baadhi ya ujenzi wa ARM64, ikiacha **RELRO gap** ndogo ambayo inaweza kuandikwa baada ya `mprotect`. Upstream sasa inalinganisha `PT_GNU_RELRO` na mipaka ya ukurasa, ikiondoa hali hiyo ya ukingo.
---
## Marejeleo
* Hati za Binutils *`-z relro`, `-z now` na `PT_GNU_RELRO`*
* *“RELRO Kamili, Sehemu na Mbinu za Kuepuka”* chapisho la blog @ wolfslittlered 2023
{{#include ../../banners/hacktricks-training.md}}