
Article de blog explorant le bac à sable macOS (App Sandbox), les entitlements via codesign, et les techniques d'évasion du bac à sable utilisant launchd, les LaunchAgents et les attributs de quarantaine.
Poursuivant ma série d'articles sur ma transition vers macOS, j'aimerais discuter un peu du bac à sable des apps de macOS.
Il est fortement recommandé de lire d'abord l'article Structure des apps macOS - je partirai du principe que le lecteur connaît la différence entre les apps, les processus (tâches), sait un peu ce qu'est launchd et son lien avec le lancement des apps.
La première fois que j'ai découvert le bac à sable de macOS, j'ai naïvement essayé de créer une macro Word malveillante.
C'est (toujours) un vecteur d'entrée très courant dans l'écosystème Windows, alors je voulais voir si je pouvais simplement lancer des processus et semer le chaos en général.
Eh bien, les choses ne sont pas si simples sur macOS - je pouvais par exemple exécuter des processus, mais il semblait qu'ils ne pouvaient pas en faire beaucoup.
Déposer des fichiers me valait toujours une erreur cryptique Operation not permitted - que se passe-t-il ?
J'ai commencé à lire un peu sur macOS et Word et je suis tombé sur cet excellent article d'Adam Chester (qui travaille chez MDSec). Je recommande vivement de lire l'article, mais je vais résumer les conclusions ici :
macOS avait autrefois un outil fonctionnel appelé sandbox-exec qui exécutait des commandes dans un bac à sable. Bien que déprécié, il pouvait mettre en évidence pas mal de choses. Dans sa page de manuel, on voit qu'il prend un profile, on peut donc en conclure que les règles du bac à sable sont maintenues dans des profils. Ces profils peuvent se présenter sous différentes formes - fichiers, noms prédéfinis ou même en tant que chaînes littérales.
Les pages de manuel indiquent également que les développeurs devraient utiliser la fonctionnalité App Sandbox. En lisant davantage, j'ai compris que les règles du bac à sable sont intégrées dans le binaire, dans notre cas, situé sous /Application/Microsoft Word.app/Contents/MacOS/Microsoft Word (si cela vous semble étranger, jetez un œil à mon article Structure des apps macOS).
Bien que vous puissiez les extraire facilement à la main, il est préférable d'utiliser un outil : 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
...
Il y a beaucoup à digérer, alors prenons quelques notes de haut niveau :
-dv, qui signifie display et verbose. Ensuite, --entitlements présente les entitlements associés à l'app ou au binaire (oui, codesign fonctionne avec les deux). Nous approfondirons les entitlements dans un autre article, mais pour l'instant disons qu'ils reflètent les capacités de l'app, et l'un d'eux indique que l'app est sandboxée (com.apple.security.app-sandbox a une valeur booléenne True).plists (encore dans mon [article Structure des apps macOS]) pourraient soupçonner que le dictionnaire clé-valeur est une représentation d'une liste de propriétés, et ils auraient raison.com.apple.security.temporary-exception.files.absolute-path.read-only mentionne un tableau de chemins absolus que l'app est autorisée à lire.Dans l'article de MDSec de 2018 que j'ai mentionné plus tôt, la partie deny file-write* sous com.apple.security.temporary-exception.sbpl n'existait pas, ce qui permettait aux macros de créer des fichiers au contenu arbitraire comme /Library/LaunchAgents/~$evil.plist. Pourquoi cela permet-il de s'évader du bac à sable ?
Les LaunchAgents et LaunchDaemons constituent un mécanisme de persistance bien connu (légitime) dans macOS. Je les ai déjà mentionnés, mais vous pouvez les considérer comme des Services (si vous venez du monde Windows) - les LaunchDaemons sont lancés au démarrage du système (et vivent donc en dehors de la session d'un utilisateur), tandis que les LaunchAgents sont lancés lorsqu'un utilisateur se connecte.
Fait intéressant, les deux sont décrits dans de simples fichiers plist. Voici un exemple pour mon programme de mise à jour OneDrive :
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
}
Ces LaunchAgents et LaunchDaemons sont lancés par launchd (vous vous souvenez de ce processus ?), ce qui permet donc de s'évader du bac à sable, car launchd n'avait aucun moyen de savoir si le plist avait été déposé par un processus sandboxé ou non (et même si c'était le cas - comment saurait-il quelles règles du bac à sable appliquer ?).
Ce concept consistant à utiliser launchd pour s'évader du bac à sable de macOS a été largement exploité, et en fait, j'ai utilisé cela par le passé.
Pour vous épargner quelques clics - voici l'idée :
launchd lance les apps de macOS. Ces apps peuvent être lancées en double-cliquant dessus, ou par d'autres moyens - par exemple, cliquer sur un fichier zip utilisera l'Archive Utility car elle est associée aux fichiers zip.launchd est la commande open.open est riche - vous pouvez utiliser certaines de ses fonctionnalités intéressantes comme sélectionner l'app, sélectionner le nom de fichier à ouvrir ou même fournir des arguments complets de ligne de commande.Python intégrée (qui n'existe plus sur les nouveaux appareils macOS de série) pour lancer Python avec un argument stdin qui redirige essentiellement l'entrée standard depuis un fichier que j'ai déposé (ce fichier était ~$evil.py en raison des contraintes de Word).launchd a exécuté une instance de l'app Python non sandboxée qui a commencé à lire , lequel contenait des commandes Python arbitraires, contournant ainsi essentiellement le bac à sable.Des idées similaires ont été présentées dans d'autres divulgations (un bel exemple se trouve ici) mais l'idée reste la même. Je suis à peu près sûr qu'il y en a bien d'autres en pleine vue !
Une mention honorable revient à un excellent article de Wojciech Regula - cette fois-ci axé sur l'app Terminal et une manipulation de variable d'environnement. Vous devriez le lire !
Le problème trouvé par MDSec était spécifique à Office - et a été corrigé avec des règles plus strictes.
Les attaques qui abusent de LaunchServices (c'est le nom du framework pour lancer des apps avec launchd) sont plus génériques - et Apple a donc dû les corriger.
Une des choses que j'ai remarquées est que les fichiers déposés par Word sont désormais créés avec l'attribut étendu com.apple.quarantine, oui, celui-là même que j'ai mentionné dans mon article introduction à Gatekeeper.
Il s'avère que cet attribut de quarantaine est un durcissement contre certaines attaques - par exemple, l'app Terminal a refusé de lancer des scripts shell créés avec cet attribut. C'est d'ailleurs la raison pour laquelle j'ai dû utiliser l'option --stdin pour Python.
Comme l'a souligné Gergely Kalman - Apple a ajouté des vérifications supplémentaires au binaire open pour durcir ce type d'exploits. Il semble que --stdin, --args et d'autres options de ligne de commande soient ignorées si le processus appelant est sandboxé. Cependant, open appelle simplement LaunchServices (dans launchd) via un IPC, et il existe commodément des API pour cela, par exemple LSOpenURLsWithRole.
Je n'ai pas vérifié si LaunchServices lui-même est également durci - sinon, je crois que des évasions de bac à sable similaires pourraient être facilement réalisées.
Nous avons brièvement discuté d'une autre technologie de macOS - le bac à sable. Nous avons vu à quel point il est puissant et configurable, et comment il peut être cassé.
Nous avons également relié certaines choses - comment les apps fonctionnent avec les règles du bac à sable, comment le lancement d'apps par launchd brise plus que les simples arborescences de processus, et comment les fichiers plist peuvent être utilisés pour le bien ou pour le mal - cette fois avec la persistance (LaunchAgents et LaunchDaemons).
Heureusement, nous avons même relié l'attribut étendu com.apple.quarantine de l'article introduction à Gatekeeper et expliqué comment il pourrait servir de durcissement supplémentaire contre les évasions du bac à sable. Pas mal !
Dans les prochains articles, nous explorerons d'autres mécanismes de sécurité de macOS et pourrions parler de stratégies pour les contourner.
Restez à l'écoute !
Jonathan Bar Or (https://jonathanbaror.com)
com.apple.security.temporary-exception.sbpl~$whatever.docx~$evil.py