Dans l'image précédente, il est possible d'observer comment le bac à sable sera chargé lorsqu'une application avec le droit com.apple.security.app-sandbox est exécutée.
Le compilateur liera /usr/lib/libSystem.B.dylib au binaire.
Ensuite, libSystem.B appellera d'autres fonctions jusqu'à ce que xpc_pipe_routine envoie les droits de l'application à securityd. Securityd vérifie si le processus doit être mis en quarantaine à l'intérieur du bac à sable, et le cas échéant, il sera mis en quarantaine.
Enfin, le bac à sable sera activé par un appel à __sandbox_ms qui appellera __mac_syscall.
Possibles contournements
Contournement de l'attribut de quarantaine
Les fichiers créés par des processus mis en bac à sable se voient attribuer l'attribut de quarantaine pour empêcher les évasions du bac à sable. Cependant, si vous parvenez à créer un dossier .app sans l'attribut de quarantaine dans une application mise en bac à sable, vous pourriez faire pointer le binaire du bundle d'application vers /bin/bash et ajouter des variables d'environnement dans le plist pour abuser de open et lancer la nouvelle application sans bac à sable.
Par conséquent, pour l'instant, si vous êtes simplement capable de créer un dossier portant un nom se terminant par .app sans attribut de quarantaine, vous pouvez échapper au bac à sable car macOS ne vérifie que l'attribut de quarantaine dans le dossier .app et dans l'exécutable principal (et nous allons faire pointer l'exécutable principal vers /bin/bash).
Notez que si un bundle .app a déjà été autorisé à s'exécuter (il a un xttr de quarantaine avec le drapeau autorisé à s'exécuter), vous pourriez également en abuser... sauf que maintenant vous ne pouvez pas écrire à l'intérieur des bundles .app à moins d'avoir certaines autorisations TCC privilégiées (que vous n'aurez pas dans un bac à sable élevé).
Même si une application est censée être mise en bac à sable (com.apple.security.app-sandbox), il est possible de contourner le bac à sable si elle est exécutée à partir d'un LaunchAgent (~/Library/LaunchAgents) par exemple.
Comme expliqué dans cet article, si vous souhaitez obtenir une persistance avec une application mise en bac à sable, vous pourriez la faire s'exécuter automatiquement en tant que LaunchAgent et peut-être injecter du code malveillant via des variables d'environnement DyLib.
Abus des emplacements de démarrage automatique
Si un processus mis en bac à sable peut écrire à un endroit où plus tard une application sans bac à sable va exécuter le binaire, il pourra s'échapper simplement en plaçant le binaire là-bas. Un bon exemple de ce type d'emplacements sont ~/Library/LaunchAgents ou /System/Library/LaunchDaemons.
Pour cela, vous pourriez même avoir besoin de 2 étapes : faire en sorte qu'un processus avec un bac à sable plus permissif (file-read*, file-write*) exécute votre code qui écrira effectivement à un endroit où il sera exécuté sans bac à sable.
Consultez cette page sur les emplacements de démarrage automatique :
Abus d'autres processus
Si à partir du processus en bac à sable vous êtes capable de compromettre d'autres processus s'exécutant dans des bacs à sable moins restrictifs (ou aucun), vous pourrez échapper à leurs bacs à sable :
Compilation statique et liaison dynamique
Cette recherche a découvert 2 façons de contourner le bac à sable. Parce que le bac à sable est appliqué depuis l'espace utilisateur lorsque la bibliothèque libSystem est chargée. Si un binaire pouvait éviter de la charger, il ne serait jamais mis en bac à sable :
Si le binaire était complètement compilé de manière statique, il pourrait éviter de charger cette bibliothèque.
Si le binaire n'avait pas besoin de charger de bibliothèques (car le lien est également dans libSystem), il n'aurait pas besoin de charger libSystem.
Shellcodes
Notez que même les shellcodes en ARM64 doivent être liés dans libSystem.dylib:
Le fichier entitlements.xml contient les autorisations spéciales accordées à une application macOS pour accéder à certaines ressources protégées du système. Ces autorisations sont définies par le développeur de l'application et doivent être approuvées par Apple lors de la soumission de l'application à l'App Store. Les entitlements peuvent inclure des autorisations telles que l'accès aux données utilisateur, l'utilisation de services système sensibles, etc. Il est essentiel de comprendre et d'analyser les entitlements d'une application pour évaluer son niveau d'accès et de sécurité.
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0">
<dict><key>com.apple.security.app-sandbox</key><true/></dict></plist>
macOS Sandbox Debug and Bypass
Debugging the macOS Sandbox
To debug the macOS sandbox, you can use the sandbox-exec tool with the -D flag to enable debug mode. This will provide detailed information about sandbox violations, helping you understand why a process is being blocked by the sandbox.
sandbox-exec-D
Bypassing the macOS Sandbox
To bypass the macOS sandbox, you can use various techniques such as exploiting vulnerabilities in the sandbox profile, injecting code into a process to disable sandbox restrictions, or manipulating the sandbox rules to allow unauthorized actions.
It is important to note that bypassing the macOS sandbox is a serious security risk and should only be done for legitimate security research purposes under controlled environments.
# Compile itgcc-Xlinker-sectcreate-Xlinker__TEXT-Xlinker__info_plist-XlinkerInfo.plistsand.c-osand# Create a certificate for "Code Signing"# Apply the entitlements via signingcodesign-s<cert-name>--entitlementsentitlements.xmlsand
L'application va essayer de lire le fichier ~/Desktop/del.txt, que le bac à sable n'autorisera pas.
Créez un fichier à cet emplacement car une fois que le bac à sable est contourné, il pourra le lire :
echo"Sandbox Bypassed">~/Desktop/del.txt
Déboguons l'application pour voir quand le Bac à sable est chargé :
# Load app in debugginglldb./sand# Set breakpoint in xpc_pipe_routine(lldb) bxpc_pipe_routine# run(lldb) r# This breakpoint is reached by different functionalities# Check in the backtrace is it was de sandbox one the one that reached it# We are looking for the one libsecinit from libSystem.B, like the following one:(lldb) bt* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1* frame #0: 0x00000001873d4178 libxpc.dylib`xpc_pipe_routineframe#1: 0x000000019300cf80 libsystem_secinit.dylib`_libsecinit_appsandbox + 584frame#2: 0x00000001874199c4 libsystem_trace.dylib`_os_activity_initiate_impl + 64frame#3: 0x000000019300cce4 libsystem_secinit.dylib`_libsecinit_initializer + 80frame#4: 0x0000000193023694 libSystem.B.dylib`libSystem_initializer + 272# To avoid lldb cutting info(lldb) settingssettarget.max-string-summary-length10000# The message is in the 2 arg of the xpc_pipe_routine function, get it with:(lldb) p (char *) xpc_copy_description($x1)(char *) $0 = 0x000000010100a400 "<dictionary: 0x6000026001e0> { count = 5, transaction: 0, voucher = 0x0, contents =\n\t\"SECINITD_REGISTRATION_MESSAGE_SHORT_NAME_KEY\" => <string: 0x600000c00d80> { length = 4, contents = \"sand\" }\n\t\"SECINITD_REGISTRATION_MESSAGE_IMAGE_PATHS_ARRAY_KEY\" => <array: 0x600000c00120> { count = 42, capacity = 64, contents =\n\t\t0: <string: 0x600000c000c0> { length = 14, contents = \"/tmp/lala/sand\" }\n\t\t1: <string: 0x600000c001e0> { length = 22, contents = \"/private/tmp/lala/sand\" }\n\t\t2: <string: 0x600000c000f0> { length = 26, contents = \"/usr/lib/libSystem.B.dylib\" }\n\t\t3: <string: 0x600000c00180> { length = 30, contents = \"/usr/lib/system/libcache.dylib\" }\n\t\t4: <string: 0x600000c00060> { length = 37, contents = \"/usr/lib/system/libcommonCrypto.dylib\" }\n\t\t5: <string: 0x600000c001b0> { length = 36, contents = \"/usr/lib/system/libcompiler_rt.dylib\" }\n\t\t6: <string: 0x600000c00330> { length = 33, contents = \"/usr/lib/system/libcopyfile.dylib\" }\n\t\t7: <string: 0x600000c00210> { length = 35, contents = \"/usr/lib/system/libcorecry"...
# The 3 arg is the address were the XPC response will be stored(lldb) registerreadx2x2=0x000000016fdfd660# Move until the end of the function(lldb) finish# Read the response## Check the address of the sandbox container in SECINITD_REPLY_MESSAGE_CONTAINER_ROOT_PATH_KEY(lldb) memoryread-fp0x000000016fdfd660-c10x16fdfd660:0x0000600003d04000(lldb) p (char *) xpc_copy_description(0x0000600003d04000)(char *) $4 = 0x0000000100204280 "<dictionary: 0x600003d04000> { count = 7, transaction: 0, voucher = 0x0, contents =\n\t\"SECINITD_REPLY_MESSAGE_CONTAINER_ID_KEY\" => <string: 0x600000c04d50> { length = 22, contents = \"xyz.hacktricks.sandbox\" }\n\t\"SECINITD_REPLY_MESSAGE_QTN_PROC_FLAGS_KEY\" => <uint64: 0xaabe660cef067137>: 2\n\t\"SECINITD_REPLY_MESSAGE_CONTAINER_ROOT_PATH_KEY\" => <string: 0x600000c04e10> { length = 65, contents = \"/Users/carlospolop/Library/Containers/xyz.hacktricks.sandbox/Data\" }\n\t\"SECINITD_REPLY_MESSAGE_SANDBOX_PROFILE_DATA_KEY\" => <data: 0x600001704100>: { length = 19027 bytes, contents = 0x0000f000ba0100000000070000001e00350167034d03c203... }\n\t\"SECINITD_REPLY_MESSAGE_VERSION_NUMBER_KEY\" => <int64: 0xaa3e660cef06712f>: 1\n\t\"SECINITD_MESSAGE_TYPE_KEY\" => <uint64: 0xaabe660cef067137>: 2\n\t\"SECINITD_REPLY_FAILURE_CODE\" => <uint64: 0xaabe660cef067127>: 0\n}"
# To bypass the sandbox we need to skip the call to __mac_syscall# Lets put a breakpoint in __mac_syscall when x1 is 0 (this is the code to enable the sandbox)(lldb) breakpointset--name__mac_syscall--condition'($x1 == 0)'(lldb) c# The 1 arg is the name of the policy, in this case "Sandbox"(lldb) memoryread-fs $x00x19300eb22:"Sandbox"## BYPASS## Due to the previous bp, the process will be stopped in:Process2517stopped* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1frame#0: 0x0000000187659900 libsystem_kernel.dylib`__mac_syscalllibsystem_kernel.dylib`:-> 0x187659900<+0>:movx16,#0x17d0x187659904<+4>:svc#0x800x187659908<+8>:b.lo0x187659928 ; <+40>0x18765990c<+12>:pacibsp# To bypass jump to the b.lo address modifying some registers first(lldb) breakpointdelete1# Remove bp(lldb) registerwrite $pc 0x187659928#b.lo address(lldb) registerwrite $x0 0x00(lldb) registerwrite $x1 0x00(lldb) registerwrite $x16 0x17d(lldb) cProcess2517resumingSandboxBypassed!Process2517exitedwithstatus=0 (0x00000000)
Même avec le contournement du bac à sable, TCC demandera à l'utilisateur s'il souhaite autoriser le processus à lire des fichiers depuis le bureau.