From 110fa225510a16ea0a60c0cd98b62257132824b0 Mon Sep 17 00:00:00 2001 From: Translator Date: Mon, 18 Aug 2025 20:37:33 +0000 Subject: [PATCH] Translated ['src/binary-exploitation/basic-stack-binary-exploitation-met --- .../elf-tricks.md | 143 ++++++++++++++---- 1 file changed, 117 insertions(+), 26 deletions(-) diff --git a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md index 77b11e3c3..f8d78e598 100644 --- a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md +++ b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/elf-tricks.md @@ -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`**. +Są 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 +#include +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}}