Translated ['src/macos-hardening/macos-security-and-privilege-escalation

This commit is contained in:
Translator 2025-07-23 10:11:48 +00:00
parent 17b98ced5c
commit 3aae561f0d

View File

@ -9,23 +9,23 @@
## 1. Przejęcie wątku
Początkowo funkcja **`task_threads()`** jest wywoływana na porcie zadania, aby uzyskać listę wątków z zdalnego zadania. Wątek jest wybierany do przejęcia. To podejście różni się od konwencjonalnych metod wstrzykiwania kodu, ponieważ tworzenie nowego zdalnego wątku jest zabronione z powodu nowej mitigacji blokującej `thread_create_running()`.
Początkowo wywoływana jest funkcja `task_threads()` na porcie zadania, aby uzyskać listę wątków z zdalnego zadania. Wątek jest wybierany do przejęcia. To podejście różni się od konwencjonalnych metod wstrzykiwania kodu, ponieważ tworzenie nowego zdalnego wątku jest zabronione z powodu zabezpieczenia, które blokuje `thread_create_running()`.
Aby kontrolować wątek, wywoływana jest **`thread_suspend()`**, zatrzymując jego wykonanie.
Aby kontrolować wątek, wywoływana jest funkcja `thread_suspend()`, zatrzymując jego wykonanie.
Jedynymi dozwolonymi operacjami na zdalnym wątku są **zatrzymywanie** i **uruchamianie** go, **pobieranie** i **modyfikowanie** wartości jego rejestrów. Zdalne wywołania funkcji są inicjowane przez ustawienie rejestrów `x0` do `x7` na **argumenty**, konfigurowanie **`pc`** w celu skierowania do pożądanej funkcji i aktywację wątku. Zapewnienie, że wątek nie ulegnie awarii po zwrocie, wymaga wykrycia zwrotu.
Jedynymi dozwolonymi operacjami na zdalnym wątku są **zatrzymywanie** i **uruchamianie** go oraz **pobieranie**/**modyfikowanie** wartości jego rejestrów. Zdalne wywołania funkcji są inicjowane przez ustawienie rejestrów `x0` do `x7` na **argumenty**, konfigurowanie `pc` w celu wskazania żądanej funkcji i wznowienie wątku. Zapewnienie, że wątek nie ulegnie awarii po zwrocie, wymaga wykrycia zwrotu.
Jedna ze strategii polega na **rejestrowaniu obsługi wyjątków** dla zdalnego wątku za pomocą `thread_set_exception_ports()`, ustawiając rejestr `lr` na nieprawidłowy adres przed wywołaniem funkcji. To wywołuje wyjątek po wykonaniu funkcji, wysyłając wiadomość do portu wyjątków, co umożliwia inspekcję stanu wątku w celu odzyskania wartości zwrotnej. Alternatywnie, jak przyjęto z exploitacji triple_fetch Iana Beera, `lr` jest ustawiane na nieskończoną pętlę. Rejestry wątku są następnie ciągle monitorowane, aż **`pc` wskaże na tę instrukcję**.
Jedna ze strategii polega na zarejestrowaniu **obsługi wyjątków** dla zdalnego wątku za pomocą `thread_set_exception_ports()`, ustawiając rejestr `lr` na nieprawidłowy adres przed wywołaniem funkcji. To wywołuje wyjątek po wykonaniu funkcji, wysyłając wiadomość do portu wyjątków, co umożliwia inspekcję stanu wątku w celu odzyskania wartości zwrotnej. Alternatywnie, jak przyjęto z exploitacji *triple_fetch* Iana Beera, `lr` jest ustawiane na nieskończoną pętlę; rejestry wątku są następnie ciągle monitorowane, aż `pc` wskaże na tę instrukcję.
## 2. Porty Mach do komunikacji
Kolejny etap polega na ustanowieniu portów Mach w celu ułatwienia komunikacji z zdalnym wątkiem. Porty te są niezbędne do transferu dowolnych praw do wysyłania i odbierania między zadaniami.
Kolejny etap polega na ustanowieniu portów Mach w celu ułatwienia komunikacji z zdalnym wątkiem. Porty te są niezbędne do transferu dowolnych praw do wysyłania/odbierania między zadaniami.
Dla komunikacji dwukierunkowej tworzone są dwa prawa odbioru Mach: jedno w lokalnym, a drugie w zdalnym zadaniu. Następnie prawo wysyłania dla każdego portu jest przekazywane do odpowiedniego zadania, co umożliwia wymianę wiadomości.
Skupiając się na lokalnym porcie, prawo odbioru jest posiadane przez lokalne zadanie. Port jest tworzony za pomocą `mach_port_allocate()`. Wyzwanie polega na przekazaniu prawa wysyłania do tego portu do zdalnego zadania.
Strategia polega na wykorzystaniu `thread_set_special_port()`, aby umieścić prawo wysyłania do lokalnego portu w `THREAD_KERNEL_PORT` zdalnego wątku. Następnie zdalny wątek jest instruowany do wywołania `mach_thread_self()`, aby odzyskać prawo wysyłania.
Strategia polega na wykorzystaniu `thread_set_special_port()`, aby umieścić prawo wysyłania do lokalnego portu w `THREAD_KERNEL_PORT` zdalnego wątku. Następnie zdalny wątek jest instruowany do wywołania `mach_thread_self()`, aby uzyskać prawo wysyłania.
Dla zdalnego portu proces jest zasadniczo odwrócony. Zdalny wątek jest kierowany do wygenerowania portu Mach za pomocą `mach_reply_port()` (ponieważ `mach_port_allocate()` jest nieodpowiednie z powodu swojego mechanizmu zwrotu). Po utworzeniu portu wywoływana jest `mach_port_insert_right()` w zdalnym wątku, aby ustanowić prawo wysyłania. To prawo jest następnie przechowywane w jądrze za pomocą `thread_set_special_port()`. W lokalnym zadaniu używa się `thread_get_special_port()` na zdalnym wątku, aby uzyskać prawo wysyłania do nowo przydzielonego portu Mach w zdalnym zadaniu.
@ -33,23 +33,23 @@ Zakończenie tych kroków skutkuje ustanowieniem portów Mach, kładąc podwalin
## 3. Podstawowe prymitywy odczytu/zapisu pamięci
W tej sekcji skupiamy się na wykorzystaniu prymitywu wykonania do ustanowienia podstawowych prymitywów odczytu i zapisu pamięci. Te początkowe kroki są kluczowe dla uzyskania większej kontroli nad zdalnym procesem, chociaż prymitywy na tym etapie nie będą miały wielu zastosowań. Wkrótce zostaną one ulepszone do bardziej zaawansowanych wersji.
W tej sekcji skupiamy się na wykorzystaniu prymitywu wykonania do ustanowienia podstawowych prymitywów odczytu/zapisu pamięci. Te początkowe kroki są kluczowe dla uzyskania większej kontroli nad zdalnym procesem, chociaż prymitywy na tym etapie nie będą miały wielu zastosowań. Wkrótce zostaną ulepszone do bardziej zaawansowanych wersji.
### Odczyt i zapis pamięci przy użyciu prymitywu wykonania
Celem jest wykonanie odczytu i zapisu pamięci przy użyciu określonych funkcji. Do odczytu pamięci używane są funkcje przypominające następującą strukturę:
Celem jest wykonanie odczytu i zapisu pamięci przy użyciu określonych funkcji. Dla **odczytu pamięci**:
```c
uint64_t read_func(uint64_t *address) {
return *address;
}
```
A do zapisywania w pamięci używane są funkcje o podobnej strukturze:
Dla **zapisu pamięci**:
```c
void write_func(uint64_t *address, uint64_t value) {
*address = value;
}
```
Te funkcje odpowiadają podanym instrukcjom asemblera:
Te funkcje odpowiadają następującemu kodowi asemblera:
```
_read_func:
ldr x0, [x0]
@ -58,104 +58,116 @@ _write_func:
str x1, [x0]
ret
```
### Identyfikacja Odpowiednich Funkcji
### Identyfikacja odpowiednich funkcji
Skanowanie powszechnych bibliotek ujawniło odpowiednich kandydatów do tych operacji:
1. **Odczyt Pamięci:**
Funkcja `property_getName()` z [biblioteki czasu wykonania Objective-C](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html) została zidentyfikowana jako odpowiednia funkcja do odczytu pamięci. Funkcja jest opisana poniżej:
1. **Odczyt pamięci — `property_getName()`** (libobjc):
```c
const char *property_getName(objc_property_t prop) {
return prop->name;
}
```
Ta funkcja działa efektywnie jak `read_func`, zwracając pierwsze pole `objc_property_t`.
2. **Pisanie do pamięci:**
Znalezienie gotowej funkcji do pisania do pamięci jest bardziej wymagające. Jednak funkcja `_xpc_int64_set_value()` z libxpc jest odpowiednim kandydatem z następującą dekompilacją:
2. **Pisanie pamięci — `_xpc_int64_set_value()`** (libxpc):
```c
__xpc_int64_set_value:
str x1, [x0, #0x18]
ret
```
Aby wykonać zapis 64-bitowy pod określonym adresem, zdalne wywołanie jest zbudowane w następujący sposób:
Aby wykonać zapis 64-bitowy pod dowolnym adresem:
```c
_xpc_int64_set_value(address - 0x18, value)
_xpc_int64_set_value(address - 0x18, value);
```
Z tymi ustalonymi prymitywami, scena jest gotowa do stworzenia pamięci współdzielonej, co stanowi znaczący postęp w kontrolowaniu zdalnego procesu.
## 4. Ustawienie Pamięci Współdzielonej
## 4. Ustawienie pamięci współdzielonej
Celem jest ustanowienie pamięci współdzielonej między lokalnymi a zdalnymi zadaniami, co upraszcza transfer danych i ułatwia wywoływanie funkcji z wieloma argumentami. Podejście polega na wykorzystaniu `libxpc` i jego typu obiektu `OS_xpc_shmem`, który oparty jest na wpisach pamięci Mach.
Celem jest ustanowienie pamięci współdzielonej między lokalnymi a zdalnymi zadaniami, co upraszcza transfer danych i ułatwia wywoływanie funkcji z wieloma argumentami. Podejście to wykorzystuje `libxpc` i jego typ obiektu `OS_xpc_shmem`, który oparty jest na wpisach pamięci Mach.
### Przegląd Procesu:
### Przegląd procesu
1. **Alokacja Pamięci**:
1. **Alokacja pamięci**
* Przydziel pamięć do współdzielenia za pomocą `mach_vm_allocate()`.
* Użyj `xpc_shmem_create()`, aby utworzyć obiekt `OS_xpc_shmem` dla przydzielonego obszaru.
2. **Tworzenie pamięci współdzielonej w zdalnym procesie**
* Przydziel pamięć dla obiektu `OS_xpc_shmem` w zdalnym procesie (`remote_malloc`).
* Skopiuj lokalny obiekt szablonu; wymagana jest nadal korekta wbudowanego prawa do wysyłania Mach w offsetcie `0x18`.
3. **Korekta wpisu pamięci Mach**
* Wstaw prawo do wysyłania za pomocą `thread_set_special_port()` i nadpisz pole `0x18` nazwą zdalnego wpisu.
4. **Finalizacja**
* Zweryfikuj zdalny obiekt i zmapuj go za pomocą zdalnego wywołania `xpc_shmem_remote()`.
- Przydziel pamięć do współdzielenia za pomocą `mach_vm_allocate()`.
- Użyj `xpc_shmem_create()`, aby utworzyć obiekt `OS_xpc_shmem` dla przydzielonego regionu pamięci. Ta funkcja zarządza tworzeniem wpisu pamięci Mach i przechowuje prawo wysyłania Mach w przesunięciu `0x18` obiektu `OS_xpc_shmem`.
## 5. Osiągnięcie pełnej kontroli
2. **Tworzenie Pamięci Współdzielonej w Zdalnym Procesie**:
Gdy dostępna jest dowolna egzekucja i kanał zwrotny pamięci współdzielonej, efektywnie posiadasz docelowy proces:
- Przydziel pamięć dla obiektu `OS_xpc_shmem` w zdalnym procesie za pomocą zdalnego wywołania `malloc()`.
- Skopiuj zawartość lokalnego obiektu `OS_xpc_shmem` do zdalnego procesu. Jednak ta początkowa kopia będzie miała niepoprawne nazwy wpisów pamięci Mach w przesunięciu `0x18`.
* **Dowolny odczyt/zapis pamięci** — użyj `memcpy()` między lokalnymi a współdzielonymi obszarami.
* **Wywołania funkcji z > 8 argumentami** — umieść dodatkowe argumenty na stosie zgodnie z konwencją wywołań arm64.
* **Transfer portu Mach** — przekazuj prawa w wiadomościach Mach za pośrednictwem ustalonych portów.
* **Transfer deskryptora pliku** — wykorzystaj fileports (zobacz *triple_fetch*).
3. **Korekta Wpisu Pamięci Mach**:
Wszystko to jest opakowane w bibliotece [`threadexec`](https://github.com/bazad/threadexec) dla łatwego ponownego użycia.
- Wykorzystaj metodę `thread_set_special_port()`, aby wstawić prawo wysyłania dla wpisu pamięci Mach do zdalnego zadania.
- Skoryguj pole wpisu pamięci Mach w przesunięciu `0x18`, nadpisując je nazwą zdalnego wpisu pamięci.
---
4. **Finalizacja Ustawienia Pamięci Współdzielonej**:
- Zweryfikuj zdalny obiekt `OS_xpc_shmem`.
- Ustanów mapowanie pamięci współdzielonej za pomocą zdalnego wywołania `xpc_shmem_remote()`.
## 6. Niuanse Apple Silicon (arm64e)
Postępując zgodnie z tymi krokami, pamięć współdzielona między lokalnymi a zdalnymi zadaniami zostanie efektywnie skonfigurowana, co umożliwi proste transfery danych i wykonanie funkcji wymagających wielu argumentów.
Na urządzeniach Apple Silicon (arm64e) **Kody uwierzytelniania wskaźników (PAC)** chronią wszystkie adresy powrotu i wiele wskaźników funkcji. Techniki przejmowania wątków, które *ponownie wykorzystują istniejący kod*, nadal działają, ponieważ oryginalne wartości w `lr`/`pc` już mają ważne podpisy PAC. Problemy pojawiają się, gdy próbujesz skoczyć do pamięci kontrolowanej przez atakującego:
## Dodatkowe Fragmenty Kodu
Do alokacji pamięci i tworzenia obiektu pamięci współdzielonej:
1. Przydziel pamięć wykonywalną wewnątrz celu (zdalne `mach_vm_allocate` + `mprotect(PROT_EXEC)`).
2. Skopiuj swój ładunek.
3. W *zdalnym* procesie podpisz wskaźnik:
```c
mach_vm_allocate();
xpc_shmem_create();
uint64_t ptr = (uint64_t)payload;
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
```
Aby utworzyć i skorygować obiekt pamięci współdzielonej w zdalnym procesie:
```c
malloc(); // for allocating memory remotely
thread_set_special_port(); // for inserting send right
4. Ustaw `pc = ptr` w przechwyconym stanie wątku.
Alternatywnie, pozostań zgodny z PAC, łącząc istniejące gadżety/funkcje (tradycyjny ROP).
## 7. Wykrywanie i wzmacnianie z EndpointSecurity
Framework **EndpointSecurity (ES)** ujawnia zdarzenia jądra, które pozwalają obrońcom obserwować lub blokować próby wstrzykiwania wątków:
* `ES_EVENT_TYPE_AUTH_GET_TASK` wyzwalane, gdy proces żąda portu innego zadania (np. `task_for_pid()`).
* `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` emitowane za każdym razem, gdy wątek jest tworzony w *innym* zadaniu.
* `ES_EVENT_TYPE_NOTIFY_THREAD_SET_STATE` (dodane w macOS 14 Sonoma) wskazuje na manipulację rejestrami istniejącego wątku.
Minimalny klient Swift, który drukuje zdarzenia zdalnych wątków:
```swift
import EndpointSecurity
let client = try! ESClient(subscriptions: [.notifyRemoteThreadCreate]) {
(_, msg) in
if let evt = msg.remoteThreadCreate {
print("[ALERT] remote thread in pid \(evt.target.pid) by pid \(evt.thread.pid)")
}
}
RunLoop.main.run()
```
Pamiętaj, aby poprawnie obsługiwać szczegóły portów Mach i nazw wpisów pamięci, aby zapewnić prawidłowe działanie konfiguracji pamięci współdzielonej.
Zapytanie z **osquery** ≥ 5.8:
```sql
SELECT target_pid, source_pid, target_path
FROM es_process_events
WHERE event_type = 'REMOTE_THREAD_CREATE';
```
### Rozważania dotyczące wzmocnionego czasu działania
## 5. Osiąganie Pełnej Kontroli
Dystrybucja aplikacji **bez** uprawnienia `com.apple.security.get-task-allow` uniemożliwia atakującym niebędącym rootem uzyskanie jej portu zadania. Ochrona integralności systemu (SIP) nadal blokuje dostęp do wielu binarnych plików Apple, ale oprogramowanie firm trzecich musi wyraźnie zrezygnować z tego.
Po pomyślnym ustanowieniu pamięci współdzielonej i uzyskaniu możliwości dowolnego wykonywania, zasadniczo zyskaliśmy pełną kontrolę nad docelowym procesem. Kluczowe funkcjonalności umożliwiające tę kontrolę to:
## 8. Ostatnie publiczne narzędzia (2023-2025)
1. **Dowolne Operacje na Pamięci**:
| Narzędzie | Rok | Uwagi |
|-----------|-----|-------|
| [`task_vaccine`](https://github.com/rodionovd/task_vaccine) | 2023 | Kompaktowy PoC, który demonstruje przejmowanie wątków z uwzględnieniem PAC na Ventura/Sonoma |
| `remote_thread_es` | 2024 | Pomocnik EndpointSecurity używany przez kilku dostawców EDR do wyświetlania zdarzeń `REMOTE_THREAD_CREATE` |
- Wykonuj dowolne odczyty pamięci, wywołując `memcpy()`, aby skopiować dane z regionu współdzielonego.
- Wykonuj dowolne zapisy pamięci, używając `memcpy()`, aby przenieść dane do regionu współdzielonego.
2. **Obsługa Wywołań Funkcji z Wieloma Argumentami**:
- Dla funkcji wymagających więcej niż 8 argumentów, umieść dodatkowe argumenty na stosie zgodnie z konwencją wywołania.
3. **Transfer Portów Mach**:
- Przenieś porty Mach między zadaniami za pomocą wiadomości Mach przez wcześniej ustanowione porty.
4. **Transfer Deskryptorów Plików**:
- Przenieś deskryptory plików między procesami, używając fileports, techniki podkreślonej przez Iana Beera w `triple_fetch`.
Ta kompleksowa kontrola jest zawarta w bibliotece [threadexec](https://github.com/bazad/threadexec), która zapewnia szczegółową implementację i przyjazne API do interakcji z procesem ofiary.
## Ważne Rozważania:
- Zapewnij prawidłowe użycie `memcpy()` do operacji odczytu/zapisu pamięci, aby utrzymać stabilność systemu i integralność danych.
- Podczas transferu portów Mach lub deskryptorów plików, przestrzegaj odpowiednich protokołów i odpowiedzialnie zarządzaj zasobami, aby zapobiec wyciekom lub niezamierzonym dostępom.
Przestrzegając tych wytycznych i wykorzystując bibliotekę `threadexec`, można efektywnie zarządzać i interagować z procesami na szczegółowym poziomie, osiągając pełną kontrolę nad docelowym procesem.
> Czytanie kodu źródłowego tych projektów jest przydatne do zrozumienia zmian w API wprowadzonych w macOS 13/14 oraz do utrzymania zgodności między Intel ↔ Apple Silicon.
## Odniesienia
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
- [https://github.com/rodionovd/task_vaccine](https://github.com/rodionovd/task_vaccine)
- [https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create](https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create)
{{#include ../../../../banners/hacktricks-training.md}}