
3 maja 2025 o 01:19 zgłosiliśmy Microsoftowi, że stworzyli lukę w mechanizmie Hardened Runtime na macOS. Nie odpowiedzieli aż do momentu, gdy 12 sierpnia 2025 o 11:40 poprosiliśmy o aktualizację. Około 5 godzin później (12 sierpnia, 16:58) otrzymaliśmy informację, że nie zamierzają jej naprawić, ponieważ nie widzą zagrożenia dla bezpieczeństwa.

No dobrze – sprawdźmy, czy naprawdę tak jest.
Hardened Runtime
W skrócie, mechanizm ten weryfikuje sygnaturę całego kodu ładowanego do procesu. Oznacza to, że atakujący nie może:
- wstrzyknąć kodu w trakcie działania aplikacji,
- zmodyfikować bibliotek w spoczynku.
Dodanie Hardened Runtime w Xcode jest proste – wystarczy włączyć odpowiednią Capability:

Po jej zaznaczeniu, do kodu zostaną dodane flagi podpisu wymuszające ochronę w trakcie działania:

Mechanizm został wprowadzony w 2018 roku i znacząco utrudnia atakowanie aplikacji firm trzecich, które go wykorzystują. Jeśli zastanawiasz się, po co w ogóle atakować aplikacje na macOS, zajrzyj do dwóch wcześniejszych artykułów:
- Zagrożenie związane z obchodzeniem TCC w systemie macOS
- RAM pamięta wszystko: niewidoczne zagrożenie w aplikacjach
Jeśli nie masz czasu na lekturę, w skrócie: celem atakującego jest eskalacja uprawnień TCC (warstwa bezpieczeństwa ponad rootem), odczytanie wrażliwych danych z pamięci procesu (np. haseł) i modyfikacja jego działania (np. przekierowanie przelewu bankowego na inne konto).
Ochrona w trakcie działania
Podczas działania aplikacji nie można uzyskać portu procesu – nawet z uprawnieniami roota – co oznacza, że nie da się wstrzyknąć kodu:

Chyba że mamy do czynienia z błędną konfiguracją zabezpieczeń, np. poprzez użycie uprawnień debugowych (opisanych w artykule „Pozwolić czy nie pozwolić na get-task-allow – oto jest pytanie„), albo z prawdziwym 0-dayem.
Ochrona bibliotek dynamicznych
Zakładamy, że czytelnik wie, czym są biblioteki dynamiczne. Jeśli nie, warto najpierw sprawdzić artykuł Snake&Apple IV — Dylibs. W skrócie: to skompilowany kod ładowany do pamięci w momencie uruchamiania programu. Przy włączonym Hardened Runtime, macOS weryfikuje podpis każdej biblioteki ładowanej podczas uruchomienia.

Jeśli podpis nie jest zgodny z oczekiwaniami aplikacji albo kod dyliba został zmodyfikowany, macOS (AMFI) zatrzyma jego uruchomianie.

Podobnie jak w przypadku Runtime Protection, tu także możliwe są błędy konfiguracji – np. podpisanie głównego pliku wykonywalnego aplikacji z uprawnieniem wyłączającym weryfikację bibliotek (com.apple.security.cs.disable-library-validation).
Podsumowanie
Apple w swojej dokumentacji jasno podkreśla, że walidacja bibliotek oparta na Hardened Runtime to kluczowy element bezpieczeństwa:

Jest równie ważna jak ochrona w trakcie działania, bo atakujący, który potrafi ją obejść, może po prostu:
- zmodyfikować bibliotekę aplikacji,
- zabić proces,
- ponownie uruchomić aplikację.
Jeśli dodatkowo aplikacja ma wbudowanego pomocnika XPC (działającego z uprawnieniami roota), obejście tego mechanizmu może prowadzić do pełnej eskalacji uprawnień do roota. W większości przypadków wystarczy to jednak do obejścia TCC i uzyskania dostępu do poufnych danych.
W dzisiejszych czasach Hardened Runtime to absolutnie kluczowy element ochrony na macOS, a każdy 0-day, który pozwala go obejść, należy uznać za poważne zagrożenie dla bezpieczeństwa systemu.
Podatność
Po omówieniu braku wiedzy i błędnej oceny Microsoftu w kwestii obejścia Library Validation, przyjrzyjmy się samej podatności, która to umożliwia. Aplikacje .NET MAUI na macOS są podatne na wstrzyknięcie kodu poprzez modyfikację DLL, nawet jeśli są podpisane z użyciem flagi Hardened Runtime. Te aplikacje nie wymuszają walidacji bibliotek dla zarządzanych asembli w katalogu MonoBundle. W praktyce oznacza to obejście mechanizmu ochronnego Hardened Runtime bez konieczności ustawiania jawnego uprawnienia com.apple.security.cs.disable-library-validation.
Nie jest to problem jednej aplikacji. KAŻDA aplikacja zbudowana w .NET MAUI jest podatna!
Szczegóły techniczne
Aplikacje .NET MAUI na macOS korzystają z frameworka Mac Catalyst do uruchamiania aplikacji wieloplatformowych:

Ogólny schemat pracy z DLL-ami w katalogu MonoBundle wygląda tak:

-
- main
- xamarin_main

xamarin_vm_initialize– run mono

xamarin_open_and_register– load exe / dll

mono_jit_exec– run Main in loaded assembly

Podatność występuje pomimo, że główny plik wykonywalny aplikacji ma włączony Hardened Runtime i poprawnie wymusza podpisywanie kodu dla natywnych bibliotek. Zarządzane DLL-e w katalogu MonoBundle nie podlegają tym samym kontrolom, w efekcie atakujący może zmodyfikować dowolny asembli w pakiecie aplikacji po podpisaniu, wstrzykując arbitralny kod, który zostanie uruchomiony przy starcie aplikacji.

Podpis DLL-a nie jest weryfikowany – w rzeczywistości pliki DLL w ogóle nie są podpisane.
PoC || GTFO
Aby zademonstrować podatność należy wykonać następujące kroki:
- Utworzyć prostą aplikację .NET MAUI (
MyFirstMauiApp.zip) według oficjalnego poradnika. - Podpisać aplikację z włączonym Hardened Runtime:
codesign --force --deep --options runtime --entitlements entitlements.plist --sign "FE7FF930DB0BC9F782DCA0FAC56BDD69A52C0B1A" MyFirstMauiApp.app- Zweryfikować podpis i aktywność Hardened Runtime – obecność flagi
flags=0x10000(runtime)potwierdza ochronę:
codesign -dvvv --entitlements :- MyFirstMauiApp.app
Executable=/Users/karmaz/MAUI/test/MyFirstMauiApp.app/Contents/MacOS/MyFirstMauiApp
Identifier=com.companyname.myfirstmauiapp
Format=app bundle with Mach-O thin (arm64)
CodeDirectory v=20500 size=71146 flags=0x10000(runtime) hashes=2212+7 location=embedded
VersionPlatform=6
VersionMin=983040
VersionSDK=1180672
Hash type=sha256 size=32
CandidateCDHash sha256=2c7a1d241bc85b8c20ebbfc681530ae4611951d3
CandidateCDHashFull sha256=2c7a1d241bc85b8c20ebbfc681530ae4611951d379761ee7508c00db64d86a9b
Hash choices=sha256
CMSDigest=2c7a1d241bc85b8c20ebbfc681530ae4611951d379761ee7508c00db64d86a9b
CMSDigestType=2
Executable Segment base=0
Executable Segment limit=5455872
Executable Segment flags=0x1
Page size=4096
CDHash=2c7a1d241bc85b8c20ebbfc681530ae4611951d3
Signature size=4788
Authority=Apple Development: kmazurek@afine.com (R5X636P694)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=3 May 2025 at 00:13:17
Info.plist entries=28
TeamIdentifier=9AL8KYY8WM
Runtime Version=18.4.0
Sealed Resources version=2 rules=13 files=237
Internal requirements count=1 size=200
[Dict]
[Key] com.apple.security.app-sandbox
[Value]
[Bool] true
[Key] com.apple.security.network.client
[Value]
[Bool] true- Zmodyfikować jedną z bibliotek w pakiecie aplikacji przy użyciu skryptu:
./inject.sh MyFirstMauiApp.app/Contents/MonoBundle/Microsoft.Extensions.Logging.dll System.ThrowHelper- Uruchomić aplikację z podmienioną biblioteką i potwierdzić wykonanie kodu poprzez obserwację żądań HTTP do kontrolowanego serwera:
127.0.0.1 - - [03/May/2025 00:02:14] code 404, message File not found
127.0.0.1 - - [03/May/2025 00:02:14] "GET /poc HTTP/1.1" 404 -Wstrzyknąć kod do znajdującego się w katalogu MonoBundle Microsoft.Extensions.Logging.dll, który wysyła żądanie HTTP, ale równie dobrze można podmienić dowolną bibliotekę i dowolny fragment kodu. Skrypt:
- Dodaje statyczny konstruktor w klasie
System.ThrowHelper, - Umieszcza w nim kod wysyłający żądanie HTTP do mojego serwera,
- Wstrzykuje payload, co potwierdziły zarówno logi skryptu, jak i logi serwera.
- Serwer zarejestrował wywołanie w momencie uruchomienia aplikacji – złośliwy kod został wykonany.
Proof of Concept na wideo:
W tym repozytorium znajdziesz przykładową aplikację i narzędzia do iniekcji.
Mitigacja
Dopóki Microsoft nie rozwiąże problemu, zaleca się nie używać aplikacji zbudowanych w .NET MAUI i nie budować w ten sposób nowych aplikacji. Obecnie nie są one zgodne z Hardened Runtime. Można je łatwo znaleźć, wyszukując katalog MonoBundle:
find / -name MonoBundleTymczasowym obejściem dla deweloperów byłoby napisanie własnych mechanizmów kontroli integralności DLL-i w kodzie aplikacji – imitujących Hardened Runtime – ale nie jest to zalecane i może prowadzić do dodatkowych błędów.
Nie sprawdzaliśmy aplikacji iOS, ale można zakładać, że one również są podatne.
Podsumowanie
Ta podatność pokazuje, jak trudne bywa wdrażanie rozwiązań wieloplatformowych takich jak .NET MAUI, które nie potrafią w pełni zintegrować się z natywnymi mechanizmami ochrony Apple. Nie obwiniamy twórców frameworka – ich zadanie i tak jest wyjątkowo trudne – ale winę ponosi zespół odpowiedzialny za wstępną ocenę zgłoszeń w Microsoft Security Response Center. Ten program bug bounty z każdym miesiącem wygląda gorzej. Przekłada się to bezpośrednio na globalne bezpieczeństwo i zauważamy w tym duży problem.
Jest jeszcze jedna rzecz. Microsoft równolegle prowadzi świetne badania nad bezpieczeństwem macOS, znajdując nowe podatności, takie jak obejścia Sandboksa czy TCC. W tym obszarze wykonują naprawdę dobrą robotę. Ale jednocześnie odmawiają naprawienia ogromnej luki, którą sami stworzyli – istniejącej od 2019 roku – umożliwiającej znacznie poważniejsze ataki. To pokrętna logika. Mamy nadzieję, że ten tekst trafi do kierownictwa MSRC, które podejmie odpowiednie działania.
Apple również zna ten problem, ale uznało, że dotyczy on wyłącznie oprogramowania firm trzecich i zaleca zgłaszanie go bezpośrednio do Microsoftu. Nie liczylibyśmy więc na to, że sami go załatają.
Referencje:
- https://developer.apple.com/documentation/xcode/configuring-the-hardened-runtime
- https://devblogs.microsoft.com/xamarin/macos-hardened-runtime-notary
- https://developer.apple.com/documentation/security/hardened-runtime
- https://dotnet.microsoft.com/en-us/learn/maui/first-app-tutorial/install
- https://github.com/Karmaz95/macOS_HR_bypass_net_maui
- https://github.com/Karmaz95/Snake_Apple




