Translated ['src/binary-exploitation/basic-stack-binary-exploitation-met

This commit is contained in:
Translator 2025-08-18 20:37:33 +00:00
parent 400a0110da
commit 110fa22551

View File

@ -37,20 +37,22 @@ Segment Sections...
07
08 .init_array .fini_array .dynamic .got
```
Poprzedni program ma **9 nagłówków programów**, a następnie **mapowanie segmentów** wskazuje, w którym nagłówku programu (od 00 do 08) **znajduje się każda sekcja**.
Poprzedni program ma **9 nagłówków programów**, a **mapowanie segmentów** wskazuje, w którym nagłówku programu (od 00 do 08) **znajduje się każda sekcja**.
### PHDR - Nagłówek Programu
Zawiera tabele nagłówków programów i same metadane.
Zawiera tabele nagłówków programów oraz metadane.
### INTERP
Wskazuje ścieżkę do loadera, który ma być użyty do załadowania binarnego do pamięci.
> Wskazówka: Statycznie linkowane lub statyczne binaria PIE nie będą miały wpisu `INTERP`. W takich przypadkach nie ma zaangażowanego dynamicznego loadera, co wyłącza techniki, które na nim polegają (np. `ret2dlresolve`).
### LOAD
Te nagłówki są używane do wskazania **jak załadować binarny do pamięci.**\
Każdy nagłówek **LOAD** wskazuje obszar **pamięci** (rozmiar, uprawnienia i wyrównanie) i wskazuje bajty ELF **binarnego do skopiowania tam**.
Każdy nagłówek **LOAD** wskazuje region **pamięci** (rozmiar, uprawnienia i wyrównanie) i wskazuje bajty ELF **binarnego do skopiowania tam**.
Na przykład, drugi ma rozmiar 0x1190, powinien znajdować się na 0x1fc48 z uprawnieniami do odczytu i zapisu i będzie wypełniony 0x528 z offsetu 0xfc48 (nie wypełnia całej zarezerwowanej przestrzeni). Ta pamięć będzie zawierać sekcje `.init_array .fini_array .dynamic .got .data .bss`.
@ -60,7 +62,13 @@ Ten nagłówek pomaga łączyć programy z ich zależnościami bibliotecznymi i
### NOTE
Przechowuje informacje metadanych dostawcy o binarnym.
Przechowuje informacje metadane dostawcy o binarnym.
- Na x86-64, `readelf -n` pokaże flagi `GNU_PROPERTY_X86_FEATURE_1_*` wewnątrz `.note.gnu.property`. Jeśli zobaczysz `IBT` i/lub `SHSTK`, binarny został zbudowany z CET (Śledzenie Pośrednich Skoków i/lub Stos Cieni). Ma to wpływ na ROP/JOP, ponieważ cele pośrednich skoków muszą zaczynać się od instrukcji `ENDBR64`, a powroty są sprawdzane w stosie cieni. Zobacz stronę CET, aby uzyskać szczegóły i notatki o obejściach.
{{#ref}}
../common-binary-protections-and-bypasses/cet-and-shadow-stack.md
{{#endref}}
### GNU_EH_FRAME
@ -70,13 +78,21 @@ Definiuje lokalizację tabel unwind stosu, używanych przez debugery i funkcje o
Zawiera konfigurację obrony przed wykonywaniem kodu ze stosu. Jeśli jest włączona, binarny nie będzie mógł wykonywać kodu ze stosu.
- Sprawdź za pomocą `readelf -l ./bin | grep GNU_STACK`. Aby wymusić przełączenie podczas testów, możesz użyć `execstack -s|-c ./bin`.
### GNU_RELRO
Wskazuje konfigurację RELRO (Relocation Read-Only) binarnego. Ta ochrona oznaczy jako tylko do odczytu niektóre sekcje pamięci (jak `GOT` lub tabele `init` i `fini`) po załadowaniu programu i przed jego uruchomieniem.
W poprzednim przykładzie kopiuje 0x3b8 bajtów do 0x1fc48 jako tylko do odczytu, wpływając na sekcje `.init_array .fini_array .dynamic .got .data .bss`.
Zauważ, że RELRO może być częściowy lub pełny, wersja częściowa nie chroni sekcji **`.plt.got`**, która jest używana do **leniwego wiązania** i potrzebuje tej przestrzeni pamięci, aby mieć **uprawnienia do zapisu**, aby zapisać adres bibliotek przy pierwszym wyszukiwaniu ich lokalizacji.
Zauważ, że RELRO może być częściowy lub pełny, wersja częściowa nie chroni sekcji **`.plt.got`**, która jest używana do **leniwego wiązania** i potrzebuje tej przestrzeni pamięci, aby mieć **uprawnienia do zapisu**, aby zapisać adres bibliotek za pierwszym razem, gdy ich lokalizacja jest wyszukiwana.
> Aby uzyskać techniki eksploatacji i aktualne notatki o obejściach, sprawdź dedykowaną stronę:
{{#ref}}
../common-binary-protections-and-bypasses/relro.md
{{#endref}}
### TLS
@ -145,21 +161,21 @@ CONTENTS, READONLY
25 .gnu_debuglink 00000034 0000000000000000 0000000000000000 000101bc 2**2
CONTENTS, READONLY
```
Wskazuje również lokalizację, przesunięcie, uprawnienia, ale także **typ danych**, który ma sekcja.
To również wskazuje lokalizację, przesunięcie, uprawnienia, ale także **typ danych**, który ma sekcja.
### Sekcje Meta
- **Tabela ciągów**: Zawiera wszystkie ciągi potrzebne przez plik ELF (ale nie te, które są faktycznie używane przez program). Na przykład zawiera nazwy sekcji takie jak `.text` lub `.data`. A jeśli `.text` znajduje się na przesunięciu 45 w tabeli ciągów, użyje liczby **45** w polu **nazwa**.
- Aby znaleźć, gdzie znajduje się tabela ciągów, ELF zawiera wskaźnik do tabeli ciągów.
- **Tabela symboli**: Zawiera informacje o symbolach, takie jak nazwa (przesunięcie w tabeli ciągów), adres, rozmiar i inne metadane dotyczące symbolu.
- **Tabela symboli**: Zawiera informacje o symbolach, takie jak nazwa (przesunięcie w tabeli ciągów), adres, rozmiar i więcej metadanych o symbolu.
### Główne Sekcje
### Sekcje Główne
- **`.text`**: Instrukcja programu do uruchomienia.
- **`.data`**: Zmienne globalne z określoną wartością w programie.
- **`.bss`**: Zmienne globalne pozostawione niezainicjowane (lub zainicjowane na zero). Zmienne tutaj są automatycznie inicjowane na zero, co zapobiega dodawaniu zbędnych zer do binarnego.
- **`.bss`**: Zmienne globalne, które nie zostały zainicjowane (lub zainicjowane na zero). Zmienne tutaj są automatycznie inicjowane na zero, co zapobiega dodawaniu zbędnych zer do binarnego.
- **`.rodata`**: Stałe zmienne globalne (sekcja tylko do odczytu).
- **`.tdata`** i **`.tbss`**: Jak .data i .bss, gdy używane są zmienne lokalne dla wątków (`__thread_local` w C++ lub `__thread` w C).
- **`.tdata`** i **`.tbss`**: Podobnie jak .data i .bss, gdy używane są zmienne lokalne dla wątków (`__thread_local` w C++ lub `__thread` w C).
- **`.dynamic`**: Zobacz poniżej.
## Symbole
@ -189,11 +205,15 @@ Każdy wpis symbolu zawiera:
- **Nazwa**
- **Atrybuty powiązania** (słaby, lokalny lub globalny): Lokalny symbol może być dostępny tylko przez sam program, podczas gdy symbole globalne są udostępniane poza programem. Słaby obiekt to na przykład funkcja, która może być nadpisana przez inną.
- **Typ**: NOTYPE (typ nieokreślony), OBJECT (globalna zmienna danych), FUNC (funkcja), SECTION (sekcja), FILE (plik źródłowy dla debuggerów), TLS (zmienna lokalna wątku), GNU_IFUNC (funkcja pośrednia do relokacji)
- **Typ**: NOTYPE (typ nieokreślony), OBJECT (globalna zmienna danych), FUNC (funkcja), SECTION (sekcja), FILE (plik źródłowy dla debuggerów), TLS (zmienna lokalna dla wątku), GNU_IFUNC (funkcja pośrednia do relokacji)
- **Indeks sekcji**, w której się znajduje
- **Wartość** (adres w pamięci)
- **Rozmiar**
#### Wersjonowanie symboli GNU (dynsym/dynstr/gnu.version)
Nowoczesny glibc używa wersji symboli. Zobaczysz wpisy w `.gnu.version` i `.gnu.version_r` oraz nazwy symboli takie jak `strlen@GLIBC_2.17`. Linker dynamiczny może wymagać konkretnej wersji przy rozwiązywaniu symbolu. Przy tworzeniu ręcznych relokacji (np. ret2dlresolve) musisz podać poprawny indeks wersji, w przeciwnym razie rozwiązywanie się nie powiedzie.
## Sekcja dynamiczna
```
readelf -d lnstat
@ -231,9 +251,26 @@ Tag Type Name/Value
```
Katalog NEEDED wskazuje, że program **musi załadować wspomnianą bibliotekę**, aby kontynuować. Katalog NEEDED kończy się, gdy wspólna **biblioteka jest w pełni operacyjna i gotowa** do użycia.
### Kolejność wyszukiwania dynamicznego loadera (RPATH/RUNPATH, $ORIGIN)
Wpisy `DT_RPATH` (przestarzałe) i/lub `DT_RUNPATH` wpływają na to, gdzie dynamiczny loader szuka zależności. Przybliżona kolejność:
- `LD_LIBRARY_PATH` (ignorowane dla programów setuid/sgid lub innych "bezpiecznych" programów)
- `DT_RPATH` (tylko jeśli `DT_RUNPATH` nieobecny)
- `DT_RUNPATH`
- `ld.so.cache`
- domyślne katalogi, takie jak `/lib64`, `/usr/lib64` itp.
`$ORIGIN` może być używane wewnątrz RPATH/RUNPATH, aby odwołać się do katalogu głównego obiektu. Z perspektywy atakującego ma to znaczenie, gdy kontrolujesz układ systemu plików lub środowisko. Dla wzmocnionych binariów (AT_SECURE) większość zmiennych środowiskowych jest ignorowana przez loader.
- Sprawdź za pomocą: `readelf -d ./bin | egrep -i 'r(path|unpath)'`
- Szybki test: `LD_DEBUG=libs ./bin 2>&1 | grep -i find` (pokazuje decyzje dotyczące ścieżki wyszukiwania)
> Wskazówka dotycząca eskalacji uprawnień: Preferuj nadużywanie zapisywalnych RUNPATHów lub źle skonfigurowanych ścieżek względnych do `$ORIGIN`, które są w twoim posiadaniu. LD_PRELOAD/LD_AUDIT są ignorowane w kontekstach bezpiecznego wykonania (setuid).
## Relokacje
Loader musi również relokować zależności po ich załadowaniu. Te relokacje są wskazane w tabeli relokacji w formatach REL lub RELA, a liczba relokacji podana jest w sekcjach dynamicznych RELSZ lub RELASZ.
Loader musi również relokować zależności po ich załadowaniu. Te relokacje są wskazane w tabeli relokacji w formatach REL lub RELA, a liczba relokacji jest podana w sekcjach dynamicznych RELSZ lub RELASZ.
```
readelf -r lnstat
@ -274,7 +311,6 @@ Offset Info Type Sym. Value Sym. Name + Addend
00000001fea0 000900000402 R_AARCH64_JUMP_SL 0000000000000000 perror@GLIBC_2.17 + 0
00000001fea8 000b00000402 R_AARCH64_JUMP_SL 0000000000000000 __cxa_finalize@GLIBC_2.17 + 0
00000001feb0 000c00000402 R_AARCH64_JUMP_SL 0000000000000000 putc@GLIBC_2.17 + 0
00000001feb8 000d00000402 R_AARCH64_JUMP_SL 0000000000000000 opendir@GLIBC_2.17 + 0
00000001fec0 000e00000402 R_AARCH64_JUMP_SL 0000000000000000 fputc@GLIBC_2.17 + 0
00000001fec8 001100000402 R_AARCH64_JUMP_SL 0000000000000000 snprintf@GLIBC_2.17 + 0
00000001fed0 001200000402 R_AARCH64_JUMP_SL 0000000000000000 __snprintf_chk@GLIBC_2.17 + 0
@ -308,13 +344,13 @@ Offset Info Type Sym. Value Sym. Name + Addend
```
### Statyczne Relokacje
Jeśli **program jest załadowany w innym miejscu** niż preferowany adres (zwykle 0x400000), ponieważ adres jest już używany lub z powodu **ASLR** lub innego powodu, statyczna relokacja **poprawia wskaźniki**, które miały wartości oczekujące, że binarny plik zostanie załadowany w preferowanym adresie.
Jeśli **program jest ładowany w innym miejscu** niż preferowany adres (zwykle 0x400000) z powodu już używanego adresu lub z powodu **ASLR** lub innego powodu, statyczna relokacja **poprawia wskaźniki**, które miały wartości oczekujące, że binarny plik zostanie załadowany w preferowanym adresie.
Na przykład każda sekcja typu `R_AARCH64_RELATIV` powinna mieć zmodyfikowany adres o wartość przesunięcia relokacji plus wartość dodaną.
Na przykład każda sekcja typu `R_AARCH64_RELATIV` powinna mieć zmodyfikowany adres na podstawie przesunięcia relokacji plus wartość addenda.
### Dynamiczne Relokacje i GOT
Relokacja może również odnosić się do zewnętrznego symbolu (jak funkcja z zależności). Na przykład funkcja malloc z libC. Wtedy, loader, ładując libC w adresie, sprawdza, gdzie funkcja malloc jest załadowana, i zapisuje ten adres w tabeli GOT (Global Offset Table) (wskazanej w tabeli relokacji), gdzie powinien być określony adres malloc.
Relokacja może również odnosić się do zewnętrznego symbolu (jak funkcja z zależności). Na przykład funkcja malloc z libC. Wtedy, loader, ładowując libC w adresie, sprawdza, gdzie funkcja malloc jest załadowana, i zapisuje ten adres w tabeli GOT (Global Offset Table) (wskazanej w tabeli relokacji), gdzie powinien być określony adres malloc.
### Tabela Łączenia Procedur
@ -322,6 +358,24 @@ Sekcja PLT pozwala na leniwe wiązanie, co oznacza, że rozwiązywanie lokalizac
Więc gdy program wywołuje malloc, tak naprawdę wywołuje odpowiednią lokalizację `malloc` w PLT (`malloc@plt`). Przy pierwszym wywołaniu rozwiązuje adres `malloc` i przechowuje go, więc następnym razem, gdy wywołana zostanie `malloc`, ten adres jest używany zamiast kodu PLT.
#### Nowoczesne zachowania łączenia, które wpływają na eksploatację
- `-z now` (Pełne RELRO) wyłącza leniwe wiązanie; wpisy PLT nadal istnieją, ale GOT/PLT jest mapowane jako tylko do odczytu, więc techniki takie jak **GOT overwrite** i **ret2dlresolve** nie będą działać przeciwko głównemu binarnemu plikowi (biblioteki mogą nadal być częściowo RELRO). Zobacz:
{{#ref}}
../common-binary-protections-and-bypasses/relro.md
{{#endref}}
- `-fno-plt` sprawia, że kompilator wywołuje zewnętrzne funkcje przez **wejście GOT bezpośrednio** zamiast przechodzić przez stub PLT. Zobaczysz sekwencje wywołań takie jak `mov reg, [got]; call reg` zamiast `call func@plt`. To zmniejsza nadużycia związane z wykonaniem spekulacyjnym i nieco zmienia polowanie na gadżety ROP wokół stubów PLT.
- PIE vs static-PIE: PIE (ET_DYN z `INTERP`) potrzebuje dynamicznego loadera i wspiera zwykłą maszynerię PLT/GOT. Static-PIE (ET_DYN bez `INTERP`) ma relokacje stosowane przez loader jądra i brak `ld.so`; oczekuj braku rozwiązywania PLT w czasie wykonywania.
> Jeśli GOT/PLT nie jest opcją, przejdź do innych zapisywalnych wskaźników kodu lub użyj klasycznego ROP/SROP w libc.
{{#ref}}
../arbitrary-write-2-exec/aw2exec-got-plt.md
{{#endref}}
## Inicjalizacja Programu
Po załadowaniu programu nadszedł czas, aby go uruchomić. Jednak pierwszy kod, który jest uruchamiany, **nie zawsze jest funkcją `main`**. Dzieje się tak, ponieważ na przykład w C++, jeśli **zmienna globalna jest obiektem klasy**, ten obiekt musi być **zainicjowany** **przed** uruchomieniem main, jak w:
@ -347,36 +401,73 @@ return 0;
```
Zauważ, że te zmienne globalne znajdują się w `.data` lub `.bss`, ale w listach `__CTOR_LIST__` i `__DTOR_LIST__` obiekty do inicjalizacji i destrukcji są przechowywane w celu ich śledzenia.
Z kodu C można uzyskać ten sam wynik, używając rozszerzeń GNU:
Z kodu C można uzyskać ten sam wynik, korzystając z rozszerzeń GNU:
```c
__attributte__((constructor)) //Add a constructor to execute before
__attributte__((destructor)) //Add to the destructor list
```
Z perspektywy kompilatora, aby wykonać te działania przed i po wykonaniu funkcji `main`, można stworzyć funkcję `init` i funkcję `fini`, które będą odniesione w sekcji dynamicznej jako **`INIT`** i **`FIN`**. i są umieszczone w sekcjach `init` i `fini` ELF.
Z perspektywy kompilatora, aby wykonać te działania przed i po wykonaniu funkcji `main`, można stworzyć funkcję `init` i funkcję `fini`, które będą odniesione w sekcji dynamicznej jako **`INIT`** i **`FIN`**. Są one umieszczane w sekcjach `init` i `fini` ELF.
Inną opcją, jak wspomniano, jest odniesienie do list **`__CTOR_LIST__`** i **`__DTOR_LIST__`** w wpisach **`INIT_ARRAY`** i **`FINI_ARRAY`** w sekcji dynamicznej, a długość tych list jest wskazywana przez **`INIT_ARRAYSZ`** i **`FINI_ARRAYSZ`**. Każdy wpis to wskaźnik do funkcji, która będzie wywoływana bez argumentów.
Ponadto, możliwe jest również posiadanie **`PREINIT_ARRAY`** z **wskaźnikami**, które będą wykonywane **przed** wskaźnikami **`INIT_ARRAY`**.
Co więcej, możliwe jest również posiadanie **`PREINIT_ARRAY`** z **wskaźnikami**, które będą wykonywane **przed** wskaźnikami **`INIT_ARRAY`**.
#### Uwagi dotyczące eksploatacji
- W przypadku Partial RELRO te tablice znajdują się w stronach, które są nadal zapisywalne, zanim `ld.so` zmieni `PT_GNU_RELRO` na tylko do odczytu. Jeśli uzyskasz dowolny zapis wystarczająco wcześnie lub możesz celować w zapisywalne tablice biblioteki, możesz przejąć kontrolę nad przepływem, nadpisując wpis funkcją według własnego wyboru. W przypadku Full RELRO są one tylko do odczytu w czasie wykonywania.
- Aby wykorzystać leniwe wiązanie dynamicznego linkera do rozwiązywania dowolnych symboli w czasie wykonywania, zobacz dedykowaną stronę:
{{#ref}}
../rop-return-oriented-programing/ret2dlresolve.md
{{#endref}}
### Kolejność inicjalizacji
1. Program jest ładowany do pamięci, statyczne zmienne globalne są inicjalizowane w **`.data`** a niezainicjowane są zerowane w **`.bss`**.
2. Wszystkie **zależności** dla programu lub bibliotek są **inicjowane** i wykonywane jest **dynamiczne linkowanie**.
2. Wszystkie **zależności** dla programu lub bibliotek są **inicjowane** i wykonywane jest **dynamiczne wiązanie**.
3. Funkcje **`PREINIT_ARRAY`** są wykonywane.
4. Funkcje **`INIT_ARRAY`** są wykonywane.
5. Jeśli istnieje wpis **`INIT`**, jest wywoływany.
6. Jeśli jest to biblioteka, dlopen kończy się tutaj, jeśli program, nadszedł czas na wywołanie **prawdziwego punktu wejścia** (funkcja `main`).
5. Jeśli istnieje wpis **`INIT`**, jest on wywoływany.
6. Jeśli jest to biblioteka, dlopen kończy się tutaj, jeśli program, nadszedł czas na wywołanie **rzeczywistego punktu wejścia** (funkcja `main`).
## Pamięć lokalna dla wątków (TLS)
Są definiowane za pomocą słowa kluczowego **`__thread_local`** w C++ lub rozszerzenia GNU **`__thread`**.
one definiowane za pomocą słowa kluczowego **`__thread_local`** w C++ lub rozszerzenia GNU **`__thread`**.
Każdy wątek będzie utrzymywał unikalną lokalizację dla tej zmiennej, więc tylko wątek może uzyskać dostęp do swojej zmiennej.
Gdy to jest używane, sekcje **`.tdata`** i **`.tbss`** są używane w ELF. Które są podobne do `.data` (zainicjowane) i `.bss` (niezainicjowane), ale dla TLS.
Gdy to jest używane, sekcje **`.tdata`** i **`.tbss`** są używane w ELF. Są one podobne do `.data` (zainicjowane) i `.bss` (niezainicjowane), ale dla TLS.
Każda zmienna będzie miała wpis w nagłówku TLS określający rozmiar i offset TLS, który jest offsetem, który będzie używany w lokalnym obszarze danych wątku.
Każda zmienna będzie miała wpis w nagłówku TLS określający rozmiar i offset TLS, który jest offsetem, którego użyje w lokalnym obszarze danych wątku.
Symbol `__TLS_MODULE_BASE` jest używany do odniesienia się do adresu bazowego pamięci lokalnej wątku i wskazuje na obszar w pamięci, który zawiera wszystkie dane lokalne wątku modułu.
`__TLS_MODULE_BASE` to symbol używany do odniesienia się do adresu bazowego pamięci lokalnej wątku i wskazuje na obszar w pamięci, który zawiera wszystkie dane lokalne wątku modułu.
## Wektor pomocniczy (auxv) i vDSO
Jądro Linuxa przekazuje wektor pomocniczy do procesów zawierający użyteczne adresy i flagi dla czasu wykonywania:
- `AT_RANDOM`: wskazuje na 16 losowych bajtów używanych przez glibc do canary stosu i innych nasion PRNG.
- `AT_SYSINFO_EHDR`: adres bazowy mapowania vDSO (przydatne do znajdowania wywołań syscalls `__kernel_*` i gadżetów).
- `AT_EXECFN`, `AT_BASE`, `AT_PAGESZ` itd.
Jako atakujący, jeśli możesz czytać pamięć lub pliki w `/proc`, często możesz wyciekować te informacje bez infoleaku w docelowym procesie:
```bash
# Show the auxv of a running process
cat /proc/$(pidof target)/auxv | xxd
# From your own process (helper snippet)
#include <sys/auxv.h>
#include <stdio.h>
int main(){
printf("AT_RANDOM=%p\n", (void*)getauxval(AT_RANDOM));
printf("AT_SYSINFO_EHDR=%p\n", (void*)getauxval(AT_SYSINFO_EHDR));
}
```
Wyciekanie `AT_RANDOM` daje ci wartość canary, jeśli możesz zdereferencjonować ten wskaźnik; `AT_SYSINFO_EHDR` daje ci bazę vDSO do wydobywania gadgetów lub do bezpośredniego wywoływania szybkich syscalls.
## References
- ld.so(8) Dynamic Loader search order, RPATH/RUNPATH, secure-execution rules (AT_SECURE): https://man7.org/linux/man-pages/man8/ld.so.8.html
- getauxval(3) Auxiliary vector and AT_* constants: https://man7.org/linux/man-pages/man3/getauxval.3.html
{{#include ../../banners/hacktricks-training.md}}