mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/binary-exploitation/common-binary-protections-and-bypas
This commit is contained in:
parent
f0e02a281c
commit
979f222bfe
@ -4,30 +4,96 @@
|
||||
|
||||
## Relro
|
||||
|
||||
**RELRO** σημαίνει **Relocation Read-Only**, και είναι μια λειτουργία ασφαλείας που χρησιμοποιείται σε δυαδικά αρχεία για να μετριάσει τους κινδύνους που σχετίζονται με τις **GOT (Global Offset Table)** υπεργραφές. Υπάρχουν δύο τύποι προστασιών **RELRO**: (1) **Partial RELRO** και (2) **Full RELRO**. Και οι δύο αναδιοργανώνουν την **GOT** και **BSS** από τα αρχεία ELF, αλλά με διαφορετικά αποτελέσματα και επιπτώσεις. Συγκεκριμένα, τοποθετούν την ενότητα **GOT** _πριν_ από την **BSS**. Δηλαδή, η **GOT** βρίσκεται σε χαμηλότερες διευθύνσεις από την **BSS**, καθιστώντας έτσι αδύνατη την υπεργραφή των εγγραφών **GOT** μέσω υπερχείλισης μεταβλητών στην **BSS** (θυμηθείτε ότι η εγγραφή στη μνήμη συμβαίνει από χαμηλότερες προς υψηλότερες διευθύνσεις).
|
||||
**RELRO** σημαίνει **Relocation Read-Only** και είναι μια μείωση που εφαρμόζεται από τον σύνδεσμο (`ld`) που καθιστά ένα υποσύνολο των δεδομένων του ELF **μόνο για ανάγνωση μετά την εφαρμογή όλων των ανακατατάξεων**. Ο στόχος είναι να σταματήσει έναν επιτιθέμενο από το να αντικαταστήσει τις καταχωρήσεις στον **GOT (Global Offset Table)** ή σε άλλες σχετικές πίνακες ανακατατάξεων που αναφέρονται κατά την εκτέλεση του προγράμματος (π.χ. `__fini_array`).
|
||||
|
||||
Ας αναλύσουμε την έννοια σε δύο διακριτούς τύπους για σαφήνεια.
|
||||
Οι σύγχρονοι σύνδεσμοι εφαρμόζουν το RELRO με το να **αναδιατάσσουν** τον **GOT** (και μερικές άλλες ενότητες) έτσι ώστε να βρίσκονται **πριν** από το **.bss** και – το πιο σημαντικό – δημιουργώντας ένα αφιερωμένο τμήμα `PT_GNU_RELRO` που ανακατανέμεται `R–X` αμέσως μετά την ολοκλήρωση της εφαρμογής των ανακατατάξεων από τον δυναμικό φορτωτή. Ως εκ τούτου, οι τυπικές υπερχειλίσεις buffer στο **.bss** δεν μπορούν πλέον να φτάσουν τον GOT και οι αυθαίρετες εγγραφές δεν μπορούν να χρησιμοποιηθούν για να αντικαταστήσουν δείκτες συναρτήσεων που βρίσκονται μέσα σε μια σελίδα προστατευμένη από RELRO.
|
||||
|
||||
### **Partial RELRO**
|
||||
Υπάρχουν **δύο επίπεδα** προστασίας που μπορεί να εκπέμψει ο σύνδεσμος:
|
||||
|
||||
**Partial RELRO** ακολουθεί μια απλούστερη προσέγγιση για να ενισχύσει την ασφάλεια χωρίς να επηρεάζει σημαντικά την απόδοση του δυαδικού αρχείου. Το Partial RELRO καθιστά **το .got μόνο για ανάγνωση (το μη-PLT μέρος της ενότητας GOT)**. Να έχετε υπόψη ότι το υπόλοιπο της ενότητας (όπως το .got.plt) είναι ακόμα εγγράψιμο και, επομένως, υπόκειται σε επιθέσεις. Αυτό **δεν αποτρέπει την κακή χρήση της GOT** από **τυχαίες ευπάθειες εγγραφής**.
|
||||
### Partial RELRO
|
||||
|
||||
Σημείωση: Από προεπιλογή, ο GCC συντάσσει δυαδικά αρχεία με Partial RELRO.
|
||||
* Παράγεται με τη σημαία `-Wl,-z,relro` (ή απλά `-z relro` όταν καλείτε απευθείας το `ld`).
|
||||
* Μόνο το **μη-PLT** μέρος του **GOT** (το μέρος που χρησιμοποιείται για τις ανακατατάξεις δεδομένων) τοποθετείται στο τμήμα μόνο για ανάγνωση. Οι ενότητες που χρειάζονται τροποποίηση κατά την εκτέλεση – το πιο σημαντικό **.got.plt** που υποστηρίζει **lazy binding** – παραμένουν εγγράψιμες.
|
||||
* Εξαιτίας αυτού, μια **αυθαίρετη εγγραφή** μπορεί ακόμα να ανακατευθύνει τη ροή εκτέλεσης αντικαθιστώντας μια καταχώρηση PLT (ή εκτελώντας **ret2dlresolve**).
|
||||
* Ο αντίκτυπος στην απόδοση είναι αμελητέος και επομένως **σχεδόν κάθε διανομή αποστέλλει πακέτα με τουλάχιστον Partial RELRO εδώ και χρόνια (είναι η προεπιλογή GCC/Binutils από το 2016)**.
|
||||
|
||||
### **Full RELRO**
|
||||
### Full RELRO
|
||||
|
||||
**Full RELRO** ενισχύει την προστασία κάνοντάς την **ολόκληρη την GOT (τόσο .got όσο και .got.plt) και την ενότητα .fini_array** εντελώς **μόνο για ανάγνωση.** Μόλις ξεκινήσει το δυαδικό αρχείο, όλες οι διευθύνσεις συναρτήσεων επιλύονται και φορτώνονται στην GOT, στη συνέχεια, η GOT σημειώνεται ως μόνο για ανάγνωση, αποτρέποντας αποτελεσματικά οποιεσδήποτε τροποποιήσεις σε αυτήν κατά τη διάρκεια της εκτέλεσης.
|
||||
* Παράγεται με **και τις δύο** σημαίες `-Wl,-z,relro,-z,now` (γνωστό και ως `-z relro -z now`). `-z now` αναγκάζει τον δυναμικό φορτωτή να επιλύσει **όλους** τους συμβολισμούς εκ των προτέρων (eager binding) έτσι ώστε το **.got.plt** να μην χρειάζεται ποτέ να γραφτεί ξανά και μπορεί να χαρτογραφηθεί με ασφάλεια μόνο για ανάγνωση.
|
||||
* Ολόκληρος ο **GOT**, **.got.plt**, **.fini_array**, **.init_array**, **.preinit_array** και μερικοί επιπλέον εσωτερικοί πίνακες glibc καταλήγουν μέσα σε ένα τμήμα μόνο για ανάγνωση `PT_GNU_RELRO`.
|
||||
* Προσθέτει μετρήσιμο κόστος εκκίνησης (όλες οι δυναμικές ανακατατάξεις επεξεργάζονται κατά την εκκίνηση) αλλά **κανένα κόστος εκτέλεσης**.
|
||||
|
||||
Ωστόσο, το trade-off με το Full RELRO είναι σε όρους απόδοσης και χρόνου εκκίνησης. Επειδή χρειάζεται να επιλύσει όλα τα δυναμικά σύμβολα κατά την εκκίνηση πριν σημειώσει την GOT ως μόνο για ανάγνωση, **τα δυαδικά αρχεία με ενεργοποιημένο το Full RELRO μπορεί να έχουν μεγαλύτερους χρόνους φόρτωσης**. Αυτή η επιπλέον επιβάρυνση εκκίνησης είναι ο λόγος που το Full RELRO δεν είναι ενεργοποιημένο από προεπιλογή σε όλα τα δυαδικά αρχεία.
|
||||
Από το 2023 πολλές κύριες διανομές έχουν αλλάξει σε σύνθεση της **εργαλειοθήκης συστήματος** (και των περισσότερων πακέτων) με **Full RELRO ως προεπιλογή** – π.χ. **Debian 12 “bookworm” (dpkg-buildflags 13.0.0)** και **Fedora 35+**. Ως pentester, θα πρέπει επομένως να περιμένετε να συναντήσετε δυαδικά αρχεία όπου **κάθε καταχώρηση GOT είναι μόνο για ανάγνωση**.
|
||||
|
||||
Είναι δυνατόν να δείτε αν το Full RELRO είναι **ενεργοποιημένο** σε ένα δυαδικό αρχείο με:
|
||||
---
|
||||
|
||||
## Πώς να ελέγξετε την κατάσταση RELRO ενός δυαδικού αρχείου
|
||||
```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` (μέρος του [pwntools](https://github.com/pwncollege/pwntools) και πολλών διανομών) αναλύει τις κεφαλίδες `ELF` και εκτυπώνει το επίπεδο προστασίας. Αν δεν μπορείτε να χρησιμοποιήσετε το `checksec`, βασιστείτε στο `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
|
||||
|
||||
Αν είναι ενεργοποιημένο το Full RELRO, ο μόνος τρόπος να το παρακάμψετε είναι να βρείτε έναν άλλο τρόπο που δεν χρειάζεται να γράψετε στον πίνακα GOT για να αποκτήσετε αυθαίρετη εκτέλεση.
|
||||
```bash
|
||||
# Full RELRO → PT_GNU_RELRO *and* the DF_BIND_NOW flag
|
||||
$ readelf -d ./vuln | grep BIND_NOW
|
||||
0x0000000000000010 (FLAGS) FLAGS: BIND_NOW
|
||||
```
|
||||
Αν το δυαδικό αρχείο εκτελείται (π.χ. ένας βοηθός set-uid root), μπορείτε να επιθεωρήσετε το εκτελέσιμο **μέσω `/proc/$PID/exe`**:
|
||||
```bash
|
||||
readelf -l /proc/$(pgrep helper)/exe | grep GNU_RELRO
|
||||
```
|
||||
---
|
||||
|
||||
Σημειώστε ότι **ο πίνακας GOT της LIBC είναι συνήθως Partial RELRO**, οπότε μπορεί να τροποποιηθεί με μια αυθαίρετη εγγραφή. Περισσότερες πληροφορίες στο [Targetting libc GOT entries](https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md#1---targetting-libc-got-entries)**.**
|
||||
## Ενεργοποίηση RELRO κατά τη σύνταξη του δικού σας κώδικα
|
||||
```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** (περασμένο μετά το `-Wl,`) και για **ld** απευθείας. Όταν χρησιμοποιείτε **CMake 3.18+** μπορείτε να ζητήσετε Full RELRO με την ενσωματωμένη προεπιλογή:
|
||||
```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")
|
||||
```
|
||||
---
|
||||
|
||||
## Τεχνικές Παράκαμψης
|
||||
|
||||
| Επίπεδο RELRO | Τυπική πρωτογενής | Πιθανές τεχνικές εκμετάλλευσης |
|
||||
|---------------|-------------------|--------------------------------|
|
||||
| Καμία / Μερική | Αυθαίρετη εγγραφή | 1. Επικαλύψτε την είσοδο **.got.plt** και αλλάξτε την εκτέλεση.<br>2. **ret2dlresolve** – δημιουργήστε ψεύτικα `Elf64_Rela` & `Elf64_Sym` σε ένα εγγράψιμο τμήμα και καλέστε το `_dl_runtime_resolve`.<br>3. Επικαλύψτε δείκτες συναρτήσεων στη λίστα **.fini_array** / **atexit()**. |
|
||||
| Πλήρης | Το GOT είναι μόνο για ανάγνωση | 1. Αναζητήστε **άλλους εγγράψιμους δείκτες κώδικα** (C++ vtables, `__malloc_hook` < glibc 2.34, `__free_hook`, callbacks σε προσαρμοσμένα τμήματα `.data`, JIT σελίδες).<br>2. Καταχρήστε *σχετικές αναγνωστικές* πρωτογενείς για να διαρρεύσετε τη libc και να εκτελέσετε **SROP/ROP στη libc**.<br>3. Εισάγετε ένα κακόβουλο κοινόχρηστο αντικείμενο μέσω **DT_RPATH**/`LD_PRELOAD` (αν το περιβάλλον ελέγχεται από τον επιτιθέμενο) ή **`ld_audit`**.<br>4. Εκμεταλλευτείτε **format-string** ή μερική επικαλύψη δείκτη για να αλλάξετε τη ροή ελέγχου χωρίς να αγγίξετε το GOT. |
|
||||
|
||||
> 💡 Ακόμα και με Πλήρη RELRO το **GOT των φορτωμένων κοινών βιβλιοθηκών (π.χ. η libc)** είναι **μόνο Μερικό RELRO** επειδή αυτά τα αντικείμενα είναι ήδη χαρτογραφημένα όταν ο φορτωτής εφαρμόζει τις ανακατατάξεις. Αν αποκτήσετε μια **αυθαίρετη εγγραφή** που μπορεί να στοχεύσει σε σελίδες άλλου κοινόχρηστου αντικειμένου, μπορείτε ακόμα να αλλάξετε την εκτέλεση επικαλύπτοντας τις εγγραφές GOT της libc ή τη στοίβα `__rtld_global`, μια τεχνική που εκμεταλλεύεται τακτικά σε σύγχρονες προκλήσεις CTF.
|
||||
|
||||
### Παράδειγμα παράκαμψης στον πραγματικό κόσμο (2024 CTF – *pwn.college “enlightened”*)
|
||||
|
||||
Η πρόκληση παραδόθηκε με Πλήρη RELRO. Η εκμετάλλευση χρησιμοποίησε ένα **off-by-one** για να διαφθείρει το μέγεθος ενός κομματιού σωρού, διέρρευσε τη libc με `tcache poisoning`, και τελικά επικαλέστηκε το `__free_hook` (εκτός του τμήματος RELRO) με ένα one-gadget για να αποκτήσει εκτέλεση κώδικα. Δεν απαιτήθηκε εγγραφή στο GOT.
|
||||
|
||||
---
|
||||
|
||||
## Πρόσφατη έρευνα & ευπάθειες (2022-2025)
|
||||
|
||||
* **glibc 2.40 αποσύρει το `__malloc_hook` / `__free_hook` (2025)** – Οι περισσότερες σύγχρονες εκμεταλλεύσεις σωρού που εκμεταλλεύτηκαν αυτά τα σύμβολα πρέπει τώρα να στραφούν σε εναλλακτικές διαδρομές όπως **`rtld_global._dl_load_jump`** ή πίνακες εξαιρέσεων C++. Επειδή οι γάντζοι βρίσκονται **εκτός** του RELRO, η αφαίρεσή τους αυξάνει τη δυσκολία των παρακάμψεων Πλήρους RELRO.
|
||||
* **Binutils 2.41 “max-page-size” διόρθωση (2024)** – Ένα σφάλμα επέτρεψε στους τελευταίους λίγους byte του τμήματος RELRO να μοιράζονται μια σελίδα με εγγράψιμα δεδομένα σε ορισμένες κατασκευές ARM64, αφήνοντας ένα μικρό **RELRO κενό** που θα μπορούσε να γραφτεί μετά το `mprotect`. Η upstream τώρα ευθυγραμμίζει το `PT_GNU_RELRO` με τα όρια σελίδων, εξαλείφοντας αυτή την περίπτωση.
|
||||
|
||||
---
|
||||
|
||||
## Αναφορές
|
||||
|
||||
* Τεκμηρίωση Binutils – *`-z relro`, `-z now` και `PT_GNU_RELRO`*
|
||||
* *“RELRO – Πλήρης, Μερική και Τεχνικές Παράκαμψης”* – ανάρτηση ιστολογίου @ wolfslittlered 2023
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user