
Blogbeitrag, der die macOS App Sandbox, Entitlements via codesign und Sandbox-Escape-Techniken mit launchd, LaunchAgents und Quarantine-Attributen untersucht.
In Fortsetzung meiner Blogpost-Serie über meinen Wechsel zu macOS möchte ich ein wenig über die App-Sandbox von macOS sprechen.
Es wird dringend empfohlen, zuerst den Blogpost zur macOS-App-Struktur zu lesen – ich setze voraus, dass der Leser den Unterschied zwischen Apps und Prozessen (Tasks) kennt, ein wenig über launchd weiß und dessen Beziehung zum Starten von Apps versteht.
Als ich zum ersten Mal von der macOS-Sandbox erfuhr, versuchte ich naiv, ein bösartiges Word-Makro zu erstellen.
Dies ist (immer noch) ein sehr verbreiteter Einstiegsvektor im Windows-Ökosystem, und ich wollte sehen, ob ich einfach Prozesse starten und generell Chaos anrichten konnte.
Nun, auf macOS sind die Dinge nicht so einfach – ich konnte zum Beispiel Prozesse ausführen, aber sie schienen nicht viel bewirken zu können.
Beim Ablegen von Dateien erhielt ich immer den kryptischen Fehler Operation not permitted – was ist da los?
Ich begann, mich ein wenig über macOS und Word zu belesen und stieß auf diesen ausgezeichneten Blogpost von Adam Chester (arbeitet bei MDSec). Ich kann den Blogpost nur wärmstens empfehlen, aber ich fasse die Erkenntnisse hier zusammen:
macOS hatte früher ein funktionierendes Dienstprogramm namens sandbox-exec, das Befehle in einer Sandbox ausführte. Obwohl es veraltet ist, konnte es einiges verdeutlichen. Auf der Manpage sieht man, dass es ein profile erhält, woraus wir schließen können, dass Sandbox-Regeln in Profilen gepflegt werden. Diese Profile können in verschiedenen Formen auftreten – als Dateien, vordefinierte Namen oder sogar als literale Zeichenketten.
Die Manpages besagen außerdem, dass Entwickler die App-Sandbox-Funktion verwenden sollten. Als ich mehr darüber las, verstand ich, dass die Sandbox-Regeln in die Binärdatei eingebettet sind, in unserem Fall unter /Application/Microsoft Word.app/Contents/MacOS/Microsoft Word (falls Ihnen das fremd erscheint, lesen Sie meinen Blogpost zur macOS-App-Struktur).
Auch wenn man sie leicht von Hand extrahieren kann, verwendet man am besten ein Werkzeug: codesign:
```jbo@McJbo ~ % codesign -dv --entitlements - /Applications/Microsoft\ Word.app/Contents/MacOS/Microsoft\ Word
Executable=/Applications/Microsoft Word.app/Contents/MacOS/Microsoft Word
Identifier=com.microsoft.Word
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=351454 flags=0x10000(runtime) hashes=10972+7 location=embedded
Signature size=8980
Timestamp=Apr 10, 2023 at 8:09:50 AM
Info.plist entries=52
TeamIdentifier=UBF8T346G9
Runtime Version=13.1.0
Sealed Resources version=2 rules=13 files=28766
Internal requirements count=1 size=180
[Dict]
[Key] com.apple.application-identifier
[Value]
[String] UBF8T346G9.com.microsoft.Word
[Key] com.apple.developer.aps-environment
[Value]
[String] production
[Key] com.apple.developer.team-identifier
[Value]
[String] UBF8T346G9
[Key] com.apple.security.app-sandbox
[Value]
[Bool] true
...
[Key] com.apple.security.temporary-exception.files.absolute-path.read-only
[Value]
[Array]
[String] /Library/Preferences/com.microsoft.office.licensingV2.plist
[String] /Library/Application Support/Microsoft/
...
[Key] com.apple.security.temporary-exception.sbpl
[Value]
[Array]
[String] (allow file-read* file-write* (require-all (vnode-type REGULAR-FILE) (regex #"(^|/)~\$[^/]+$")) )
[String] (deny file-write* (subpath (string-append (param "_HOME") "/Library/Application Scripts")) (subpath (string-append (param "_HOME") "/Library/LaunchAgents")) )
[Key] com.apple.security.temporary-exception.shared-preference.read-only
[Value]
[Array]
[String] com.ThomsonResearchSoft.EndNote
...
Das ist eine Menge Stoff, also halten wir einige allgemeine Punkte fest:
-dv, was für display und verbose steht. Dann zeigt --entitlements die Entitlements an, die mit der App oder der Binärdatei verknüpft sind (ja, codesign kann mit beiden arbeiten). Wir werden uns in einem anderen Blogpost mit Entitlements befassen, aber für den Moment sagen wir, sie spiegeln die Fähigkeiten der App wider, und eines davon besagt, dass die App in einer Sandbox läuft (com.apple.security.app-sandbox hat den booleschen Wert True).plists geschimpft habe (wieder in meinem [Blogpost zur macOS-App-Struktur]), könnte vermuten, dass das Schlüssel-Wert-Dictionary eine Darstellung einer Property List ist – und damit richtig liegen.com.apple.security.temporary-exception.files.absolute-path.read-only ein Array von absoluten Pfaden, die die App lesen darf.In dem MDSec-Blogpost von 2018, den ich vorhin erwähnt habe, existierte der Teil deny file-write* unter com.apple.security.temporary-exception.sbpl noch nicht, was es Makros erlaubte, Dateien mit beliebigem Inhalt wie /Library/LaunchAgents/~$evil.plist zu erstellen. Warum führt das zum Sandbox-Escape?
LaunchAgents und LaunchDaemons sind ein bekannter (legitimer) Persistenzmechanismus in macOS. Ich habe sie bereits erwähnt, aber man kann sie sich wie Dienste vorstellen (wenn man aus der Windows-Welt kommt) – LaunchDaemons starten, wenn das Betriebssystem bootet (und laufen daher außerhalb der Benutzersitzung), während LaunchAgents starten, wenn sich ein Benutzer anmeldet.
Interessanterweise werden beide in einfachen plist-Dateien beschrieben. Hier ein Beispiel für meinen OneDrive-Updater:
jbo@McJbo ~ % plutil -p /Library/LaunchAgents/com.microsoft.OneDriveStandaloneUpdater.plist
{
"Label" => "com.microsoft.OneDriveStandaloneUpdater"
"Program" => "/Applications/OneDrive.app/Contents/StandaloneUpdater.app/Contents/MacOS/OneDriveStandaloneUpdater"
"ProgramArguments" => [
]
"RunAtLoad" => 1
"StartInterval" => 86400
}
Diese LaunchAgents und LaunchDaemons werden von launchd gestartet (erinnern Sie sich an diesen Prozess?) und umgehen dadurch die Sandbox, da launchd nicht wusste, ob die plist von einem sandboxed-Prozess abgelegt wurde oder nicht (und selbst wenn es das wüsste – woher sollte es wissen, welche Sandbox-Regeln anzuwenden sind?).
Dieses Konzept, launchd zum Umgehen der macOS-Sandbox zu nutzen, wurde ausgiebig verwendet, und tatsächlich habe ich es in der Vergangenheit selbst eingesetzt.
Um Ihnen ein paar Klicks zu ersparen – hier ist die Idee:
launchd macOS-Apps. Diese Apps können per Doppelklick oder auf andere Weise gestartet werden – zum Beispiel verwendet das Anklicken einer zip-Datei das Archive Utility, da es mit zip-Dateien verknüpft ist.launchd zu starten, ist der Befehl open.open ist mächtig – man kann einige seiner interessanten Funktionen nutzen, etwa die App auswählen, den zu öffnenden Dateinamen wählen oder sogar vollständige Befehlszeilenargumente übergeben.Python-App verwendet (die auf neuen nativen macOS-Geräten nicht mehr existiert), um Python mit einem stdin-Argument zu starten, das im Wesentlichen die Standardeingabe von einer von mir abgelegten Datei umleitet (diese Datei war wegen der Einschränkungen von Word ~$evil.py).launchd eine nicht-sandboxed Instanz der Python-App aus, die begann, aus zu lesen, in dem beliebige Python-Befehle standen – das umging im Wesentlichen die Sandbox.Ähnliche Ideen gab es auch in anderen Offenlegungen (ein schönes Beispiel hier), aber die Idee bleibt dieselbe. Ich bin mir ziemlich sicher, dass es noch viele weitere gibt, die offen zutage liegen!
Eine besondere Erwähnung verdient ein großartiger Blogpost von Wojciech Regula – diesmal mit Fokus auf die Terminal-App und eine Manipulation von Umgebungsvariablen. Sie sollten ihn lesen!
Das von MDSec gefundene Problem war spezifisch für Office – und wurde mit strengeren Regeln behoben.
Diejenigen, die LaunchServices missbrauchten (das ist der Name des Frameworks zum Starten von Apps mit launchd), sind allgemeiner – und daher musste Apple sie beheben.
Eines der Dinge, die mir aufgefallen sind: Von Word abgelegte Dateien werden jetzt mit dem erweiterten Attribut com.apple.quarantine erstellt – ja, demselben, das ich in meinem Blogpost zur Einführung in Gatekeeper erwähnt habe.
Wie sich herausstellt, ist dieses Quarantäne-Attribut eine Härtung gegen bestimmte Angriffe – zum Beispiel weigerte sich die Terminal-App, Shell-Skripte auszuführen, die mit diesem Attribut erstellt wurden. Das ist übrigens der Grund, warum ich die Option --stdin für Python verwenden musste.
Wie Gergely Kalman anmerkte, hat Apple weitere Prüfungen zur open-Binärdatei hinzugefügt, um solche Exploits zu erschweren. Es scheint, dass --stdin, --args und andere Befehlszeilenoptionen ignoriert werden, wenn der aufrufende Prozess in einer Sandbox läuft. Allerdings ruft open einfach LaunchServices (in launchd) über IPC auf, und praktischerweise gibt es dafür APIs, z. B. LSOpenURLsWithRole.
Ich habe nicht untersucht, ob LaunchServices selbst ebenfalls gehärtet ist – wenn nicht, glaube ich, dass ähnliche Sandbox-Escapes leicht zu erreichen wären.
Wir haben kurz eine weitere macOS-Technologie besprochen – die Sandbox. Wir haben gesehen, wie mächtig und konfigurierbar sie ist und wie sie gebrochen werden kann.
Wir haben auch einige Dinge miteinander verknüpft – wie Apps mit Sandbox-Regeln arbeiten, wie das Starten von Apps durch launchd mehr als nur die Prozessbäume aufbricht und wie plist-Dateien zum Guten oder Bösen verwendet werden können – dieses Mal mit Persistenz (LaunchAgents und LaunchDaemons).
Zum Glück haben wir sogar das erweiterte Attribut com.apple.quarantine aus dem Blogpost zur Einführung in Gatekeeper einbezogen und erklärt, wie es als zusätzliche Härtung gegen Sandbox-Escapes dienen könnte. Nicht schlecht!
In den nächsten Blogposts werden wir weitere Sicherheitsmechanismen in macOS untersuchen und möglicherweise über Strategien zu deren Überwindung sprechen.
Bleiben Sie dran!
Jonathan Bar Or (https://jonathanbaror.com)
com.apple.security.temporary-exception.sbpl~$whatever.docx~$evil.py