# Wstrzykiwanie bibliotek w macOS
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF**, sprawdź [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) **i** [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) **repozytoriów GitHub**.
{% hint style="danger" %}
Kod **dyld jest open source** i można go znaleźć pod adresem [https://opensource.apple.com/source/dyld/](https://opensource.apple.com/source/dyld/), a można go pobrać jako tar za pomocą **URL-a takiego jak** [https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz](https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz)
{% endhint %}
## **DYLD\_INSERT\_LIBRARIES**
Jest to odpowiednik [**LD\_PRELOAD w Linuxie**](../../../../linux-hardening/privilege-escalation#ld\_preload). Pozwala wskazać procesowi, który ma zostać uruchomiony, aby załadować określoną bibliotekę z określonej ścieżki (jeśli zmienna środowiskowa jest włączona).
Ta technika może być również **używana jako technika ASEP**, ponieważ każda zainstalowana aplikacja ma plik plist o nazwie "Info.plist", który umożliwia **przypisanie zmiennych środowiskowych** za pomocą klucza `LSEnvironmental`.
{% hint style="info" %}
Od 2012 roku **Apple znacznie ograniczył moc** **`DYLD_INSERT_LIBRARIES`**.
Przejdź do kodu i **sprawdź `src/dyld.cpp`**. W funkcji **`pruneEnvironmentVariables`** można zobaczyć, że zmienne **`DYLD_*`** są usuwane.
W funkcji **`processRestricted`** ustawiono powód ograniczenia. Sprawdzając ten kod, można zobaczyć, że powodami są:
* Binarny plik jest `setuid/setgid`
* Istnienie sekcji `__RESTRICT/__restrict` w binarnym pliku macho.
* Oprogramowanie ma uprawnienia (hardened runtime) bez uprawnienia [`com.apple.security.cs.allow-dyld-environment-variables`](https://developer.apple.com/documentation/bundleresources/entitlements/com\_apple\_security\_cs\_allow-dyld-environment-variables)
* Sprawdź **uprawnienia** binarnego pliku za pomocą polecenia: `codesign -dv --entitlements :- `
W nowszych wersjach tej logiki można znaleźć w drugiej części funkcji **`configureProcessRestrictions`.** Jednakże, to, co jest wykonywane w nowszych wersjach, to **początkowe sprawdzenia funkcji** (można usunąć ify związane z iOS lub symulacją, ponieważ nie będą one używane w macOS.
{% endhint %}
### Walidacja bibliotek
Nawet jeśli binarny plik pozwala na użycie zmiennej środowiskowej **`DYLD_INSERT_LIBRARIES`**, jeśli binarny plik sprawdza podpis biblioteki, aby ją załadować, nie załaduje niestandardowej biblioteki.
Aby załadować niestandardową bibliotekę, binarny plik musi mieć **jedno z następujących uprawnień**:
* [`com.apple.security.cs.disable-library-validation`](../../macos-security-protections/macos-dangerous-entitlements.md#com.apple.security.cs.disable-library-validation)
* [`com.apple.private.security.clear-library-validation`](../../macos-security-protections/macos-dangerous-entitlements.md#com.apple.private.security.clear-library-validation)
lub binarny plik **nie powinien** mieć flagi **hardened runtime** ani flagi **walidacji biblioteki**.
Możesz sprawdzić, czy binarny plik ma **hardened runtime** za pomocą polecenia `codesign --display --verbose `, sprawdzając flagę runtime w **`CodeDirectory`** tak jak: **`CodeDirectory v=20500 size=767 flags=0x10000(runtime) hashes=13+7 location=embedded`**
Możesz również załadować bibliotekę, jeśli jest **podpisana tym samym certyfikatem co binarny plik**.
Znajdź przykład, jak (nadużywać) to wykorzystać i sprawdzić ograniczenia w:
{% content-ref url="../../macos-dyld-hijacking-and-dyld_insert_libraries.md" %}
[macos-dyld-hijacking-and-dyld\_insert\_libraries.md](../../macos-dyld-hijacking-and-dyld\_insert\_libraries.md)
{% endcontent-ref %}
## Przechwytywanie dylib
{% hint style="danger" %}
Pamiętaj, że **dotyczą cię również wcześniejsze ograniczenia walidacji bibliotek**, aby przeprowadzić ataki przechwytywania dylib.
{% endhint %}
Podobnie jak w systemie Windows, w systemie MacOS można również **przechwycić dyliby**, aby **aplikacje wykonywały** **arbitralny** **kod** (w rzeczywistości od zwykłego użytkownika to może być niemożliwe, ponieważ może być wymagane uprawnienie TCC do zapisu wewnątrz pakietu `.app` i przechwycenia biblioteki).\
Jednak sposób, w jaki **aplikacje MacOS** ładują **biblioteki**, jest **bardziej ograniczony** niż w systemie Windows. Oznacza to, że deweloperzy **złośliwego oprogramowania** wciąż mogą używać tej techniki w celu **ukrycia**, ale prawdopodobieństwo wykorzystania jej do **eskalacji uprawnień jest znacznie niższe**.
Po pierwsze, **częściej** można znaleźć, że **binarne pliki MacOS wskazują pełną ścieżkę** do bibliotek do załadowania. Po drugie, **MacOS nigdy nie przeszukuje** folderów **$PATH** w poszukiwaniu bibliotek.
**Główna** część **kodu** związana z tą funkcjonalnością znajduje się w **`ImageLoader::recursiveLoadLibraries`** w pliku `ImageLoader.cpp`.
Istnieją **4 różne polecenia nagłówka**, które binarny pl
* Jeśli **`LC_LOAD_DYLIB`** zawiera `@rpath/library.dylib`, a **`LC_RPATH`** zawiera `/application/app.app/Contents/Framework/v1/` i `/application/app.app/Contents/Framework/v2/`, oba foldery zostaną użyte do załadowania `library.dylib`. Jeśli biblioteka nie istnieje w `[...]/v1/`, atakujący może umieścić ją tam, aby przejąć załadowanie biblioteki w `[...]/v2/`, ponieważ ścieżki w **`LC_LOAD_DYLIB`** są przestrzegane.
* **Znajdź ścieżki rpath i biblioteki** w plikach binarnych za pomocą polecenia: `otool -l | grep -E "LC_RPATH|LC_LOAD_DYLIB" -A 5`
{% hint style="info" %}
**`@executable_path`**: Jest to **ścieżka** do katalogu zawierającego **główny plik wykonywalny**.
**`@loader_path`**: Jest to **ścieżka** do **katalogu** zawierającego **binarny Mach-O**, który zawiera polecenie ładowania.
* Gdy jest używane w pliku wykonywalnym, **`@loader_path`** jest efektywnie **takie samo** jak **`@executable_path`**.
* Gdy jest używane w **dylib**, **`@loader_path`** daje **ścieżkę** do **dylib**.
{% endhint %}
Sposób na **przywileje eskalacji** poprzez wykorzystanie tej funkcjonalności polega na rzadkim przypadku, gdy **aplikacja** uruchamiana **przez** **roota** poszukuje pewnej **biblioteki w folderze, w którym atakujący ma uprawnienia do zapisu**.
{% hint style="success" %}
Świetnym **skanerem** do znajdowania **brakujących bibliotek** w aplikacjach jest [**Dylib Hijack Scanner**](https://objective-see.com/products/dhs.html) lub [**wersja CLI**](https://github.com/pandazheng/DylibHijack).\
Świetny **raport z technicznymi szczegółami** na temat tej techniki można znaleźć [**tutaj**](https://www.virusbulletin.com/virusbulletin/2015/03/dylib-hijacking-os-x).
{% endhint %}
**Przykład**
{% content-ref url="../../macos-dyld-hijacking-and-dyld_insert_libraries.md" %}
[macos-dyld-hijacking-and-dyld\_insert\_libraries.md](../../macos-dyld-hijacking-and-dyld\_insert\_libraries.md)
{% endcontent-ref %}
## Przechwytywanie Dlopen
{% hint style="danger" %}
Pamiętaj, że **dotyczą również wcześniejsze ograniczenia weryfikacji bibliotek**, aby przeprowadzić ataki przechwytywania Dlopen.
{% endhint %}
Z dokumentacji **`man dlopen`**:
* Gdy ścieżka **nie zawiera znaku ukośnika** (czyli jest to tylko nazwa pliku), **dlopen() będzie przeszukiwać**. Jeśli **`$DYLD_LIBRARY_PATH`** został ustawiony podczas uruchamiania, dyld najpierw **sprawdzi ten katalog**. Następnie, jeśli wywołujący plik mach-o lub główny plik wykonywalny określają **`LC_RPATH`**, dyld będzie **szukać w tych** katalogach. Następnie, jeśli proces jest **nierestrykcyjny**, dyld będzie szukać w **bieżącym katalogu roboczym**. Na koniec, dla starych plików binarnych, dyld spróbuje kilku alternatyw. Jeśli **`$DYLD_FALLBACK_LIBRARY_PATH`** został ustawiony podczas uruchamiania, dyld będzie szukać w **tych katalogach**, w przeciwnym razie dyld będzie szukać w **`/usr/local/lib/`** (jeśli proces jest nierestrykcyjny), a następnie w **`/usr/lib/`** (te informacje zostały zaczerpnięte z **`man dlopen`**).
1. `$DYLD_LIBRARY_PATH`
2. `LC_RPATH`
3. `CWD` (jeśli nierestrykcyjny)
4. `$DYLD_FALLBACK_LIBRARY_PATH`
5. `/usr/local/lib/` (jeśli nierestrykcyjny)
6. `/usr/lib/`
{% hint style="danger" %}
Jeśli brak ukośników w nazwie, istnieją 2 sposoby na przechwycenie:
* Jeśli dowolne **`LC_RPATH`** jest **zapisywalne** (ale podpis jest sprawdzany, więc do tego potrzebujesz również, aby plik binarny był nierestrykcyjny)
* Jeśli plik binarny jest **nierestrykcyjny**, a następnie można załadować coś z CWD (lub nadużyć jednej z wymienionych zmiennych środowiskowych)
{% endhint %}
* Gdy ścieżka **wygląda jak ścieżka do frameworka** (np. `/stuff/foo.framework/foo`), jeśli **`$DYLD_FRAMEWORK_PATH`** został ustawiony podczas uruchamiania, dyld najpierw będzie szukać w tym katalogu **częściowej ścieżki frameworka** (np. `foo.framework/foo`). Następnie dyld spróbuje **podanej ścieżki** (używając bieżącego katalogu roboczego dla ścieżek względnych). Na koniec, dla starych plików binarnych, dyld spróbuje kilku alternatyw. Jeśli **`$DYLD_FALLBACK_FRAMEWORK_PATH`** został ustawiony podczas uruchamiania, dyld będzie szukać w tych katalogach. W przeciwnym razie, dyld będzie szukać w **`/Library/Frameworks`** (na macOS, jeśli proces jest nierestrykcyjny), a następnie w **`/System/Library/Frameworks`**.
1. `$DYLD_FRAMEWORK_PATH`
2. podana ścieżka (używając bieżącego katalogu roboczego dla ścieżek względnych, jeśli nierestrykcyjny)
3. `$DYLD_FALLBACK_FRAMEWORK_PATH`
4. `/Library/Frameworks` (jeśli nierestrykcyjny)
5. `/System/Library/Frameworks`
{% hint style="danger" %}
Jeśli ścieżka frameworka, sposób na przechwycenie to:
* Jeśli proces jest **nierestrykcyjny**, nadużywając **ścieżki względnej z CWD** i wspomnianych zmiennych środowiskowych (nawet jeśli nie jest to wspomniane w dokumentacji, czy proces jest ograniczony, zmienne środowiskowe DYLD\_\* są usuwane)
{% endhint %}
* Gdy ścieżka **zawiera ukośnik, ale nie jest ścieżką do frameworka** (czyli pełna ścieżka lub częściowa ścieżka do dylib), dlopen() najpierw szuka (jeśli ustawiono) w **`$DYLD_LIBRARY_PATH`** (z częścią liściową ze ścieżki). Następnie dyld **sprawdza podaną ścieżkę** (używając bieżącego katalogu roboczego dla ścieżek względnych (ale tylko dla nierestrykcyjnych procesów)). Na koniec, dla starszych plików binarnych, dyld spróbuje kilku alternatyw. Jeśli **`$DYLD_FALLBACK_LIBRARY_PATH`** został ustawiony podczas uruchamiania, dyld będzie szukać w tych katalogach, w przeciwnym razie dyld będzie szukać w **`/usr/local/lib/`** (jeśli proces jest nierestrykcyjny), a następnie w **`/usr/lib/`**.
1. `$DYLD_LIBRARY_PATH`
2. podana ścieżka (używając bieżącego katalogu roboczego dla ścieżek względnych, jeśli nierestrykcyjny)
3. `$DYLD_FALLBACK_LIBRARY_PATH`
4. `/usr/local/lib/` (jeśli nierestrykcyjny)
5. `/usr/lib/`
{% hint style="danger" %}
Jeśli w nazwie są ukośniki i nie jest to ścieżka do frameworka, sposób na przechwycenie to:
* Jeśli plik binarny jest **nierestrykcyjny**, a następnie można załadować coś z CWD lub `/usr/local/lib` (l
```c
// gcc dlopentest.c -o dlopentest -Wl,-rpath,/tmp/test
#include
#include
int main(void)
{
void* handle;
fprintf("--- No slash ---\n");
handle = dlopen("just_name_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n\n\n", dlerror());
}
fprintf("--- Relative framework ---\n");
handle = dlopen("a/framework/rel_framework_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n\n\n", dlerror());
}
fprintf("--- Abs framework ---\n");
handle = dlopen("/a/abs/framework/abs_framework_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n\n\n", dlerror());
}
fprintf("--- Relative Path ---\n");
handle = dlopen("a/folder/rel_folder_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n\n\n", dlerror());
}
fprintf("--- Abs Path ---\n");
handle = dlopen("/a/abs/folder/abs_folder_dlopentest.dylib",1);
if (!handle) {
fprintf(stderr, "Error loading: %s\n\n\n", dlerror());
}
return 0;
}
```
Jeśli go skompilujesz i uruchomisz, możesz zobaczyć **gdzie nieudanie wyszukiwano każdej biblioteki**. Możesz również **filtrować dzienniki systemu plików**:
```bash
sudo fs_usage | grep "dlopentest"
```
## Wykorzystywanie względnej ścieżki
Jeśli **uprzywilejowany plik/aplikacja** (takie jak SUID lub jakiś plik binarny z potężnymi uprawnieniami) **ładuje bibliotekę za pomocą względnej ścieżki** (na przykład używając `@executable_path` lub `@loader_path`) i ma wyłączoną weryfikację bibliotek, możliwe jest przeniesienie pliku binarnego do lokalizacji, w której atakujący może **modyfikować ładowaną bibliotekę o względnej ścieżce** i wykorzystać ją do wstrzykiwania kodu w proces.
## Usuwanie zmiennych środowiskowych `DYLD_*` i `LD_LIBRARY_PATH`
W pliku `dyld-dyld-832.7.1/src/dyld2.cpp` można znaleźć funkcję **`pruneEnvironmentVariables`**, która usuwa wszystkie zmienne środowiskowe, które **zaczynają się od `DYLD_`** i **`LD_LIBRARY_PATH=`**.
Funkcja ta również ustawia na **null** konkretne zmienne środowiskowe **`DYLD_FALLBACK_FRAMEWORK_PATH`** i **`DYLD_FALLBACK_LIBRARY_PATH`** dla plików binarnych **suid** i **sgid**.
Ta funkcja jest wywoływana z funkcji **`_main`** tego samego pliku, jeśli jest ukierunkowana na system operacyjny OSX w ten sposób:
```cpp
#if TARGET_OS_OSX
if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
pruneEnvironmentVariables(envp, &apple);
```
i te flagi boolean są ustawiane w tym samym pliku w kodzie:
```cpp
#if TARGET_OS_OSX
// support chrooting from old kernel
bool isRestricted = false;
bool libraryValidation = false;
// any processes with setuid or setgid bit set or with __RESTRICT segment is restricted
if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) {
isRestricted = true;
}
bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0);
uint32_t flags;
if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) {
// On OS X CS_RESTRICT means the program was signed with entitlements
if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) {
isRestricted = true;
}
// Library Validation loosens searching but requires everything to be code signed
if ( flags & CS_REQUIRE_LV ) {
isRestricted = false;
libraryValidation = true;
}
}
gLinkContext.allowAtPaths = !isRestricted;
gLinkContext.allowEnvVarsPrint = !isRestricted;
gLinkContext.allowEnvVarsPath = !isRestricted;
gLinkContext.allowEnvVarsSharedCache = !libraryValidation || !usingSIP;
gLinkContext.allowClassicFallbackPaths = !isRestricted;
gLinkContext.allowInsertFailures = false;
gLinkContext.allowInterposing = true;
```
Co w zasadzie oznacza, że jeśli binarny plik jest **suid** lub **sgid**, lub ma segment **RESTRICT** w nagłówkach lub został podpisany flagą **CS\_RESTRICT**, to **`!gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache`** jest prawdziwe i zmienne środowiskowe są usuwane.
Należy zauważyć, że jeśli CS\_REQUIRE\_LV jest prawdziwe, to zmienne nie zostaną usunięte, ale weryfikacja biblioteki sprawdzi, czy używają tego samego certyfikatu co oryginalny plik binarny.
## Sprawdź ograniczenia
### SUID i SGID
```bash
# Make it owned by root and suid
sudo chown root hello
sudo chmod +s hello
# Insert the library
DYLD_INSERT_LIBRARIES=inject.dylib ./hello
# Remove suid
sudo chmod -s hello
```
### Sekcja `__RESTRICT` z segmentem `__restrict`
The `__RESTRICT` section is a segment in macOS that is used to restrict the loading of libraries into a process. This section is designed to prevent library injection attacks, where an attacker injects malicious code into a process by loading a malicious library.
When a library is loaded into a process, the dynamic linker checks if the library has a `__RESTRICT` section. If it does, the dynamic linker will refuse to load the library into the process. This prevents any unauthorized libraries from being loaded and executed within the process.
The `__RESTRICT` section is typically used by system libraries and frameworks to protect themselves from library injection attacks. By including a `__RESTRICT` section in their binaries, these libraries ensure that only trusted libraries are loaded into their processes.
It is important for developers to be aware of the `__RESTRICT` section and use it in their own libraries to enhance the security of their applications. By including a `__RESTRICT` section, developers can prevent unauthorized libraries from being loaded into their processes, thereby reducing the risk of library injection attacks.
To summarize, the `__RESTRICT` section with the segment `__restrict` is a security feature in macOS that helps prevent library injection attacks by restricting the loading of libraries into a process. Developers should utilize this feature to enhance the security of their applications.
```bash
gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-restrict
```
### Zabezpieczony runtime
Utwórz nowy certyfikat w Keychain i użyj go do podpisania pliku binarnego:
{% code overflow="wrap" %}
```bash
# Apply runtime proetction
codesign -s --option=runtime ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello #Library won't be injected
# Apply library validation
codesign -f -s --option=library ./hello
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed #Will throw an error because signature of binary and library aren't signed by same cert (signs must be from a valid Apple-signed developer certificate)
# Sign it
## If the signature is from an unverified developer the injection will still work
## If it's from a verified developer, it won't
codesign -f -s inject.dylib
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed
# Apply CS_RESTRICT protection
codesign -f -s --option=restrict hello-signed
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-signed # Won't work
```
{% endcode %}
{% hint style="danger" %}
Należy zauważyć, że nawet jeśli istnieją binarne pliki podpisane flagami **`0x0(none)`**, mogą one dynamicznie otrzymać flagę **`CS_RESTRICT`** podczas wykonywania i dlatego ta technika w nich nie zadziała.
Możesz sprawdzić, czy proces ma tę flagę za pomocą (pobierz [**csops tutaj**](https://github.com/axelexic/CSOps)):
```bash
csops -status
```
a następnie sprawdź, czy flaga 0x800 jest włączona.
{% endhint %}
## Referencje
* [https://theevilbit.github.io/posts/dyld_insert_libraries_dylib_injection_in_macos_osx_deep_dive/](https://theevilbit.github.io/posts/dyld_insert_libraries_dylib_injection_in_macos_osx_deep_dive/)
Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!
Inne sposoby wsparcia HackTricks:
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF**, sprawdź [**PLAN SUBSKRYPCJI**](https://github.com/sponsors/carlospolop)!
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
* **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.