
Post del blog che esplora il macOS App Sandbox, gli entitlements tramite codesign e le tecniche di evasione della sandbox usando launchd, LaunchAgents e attributi di quarantena.
Continuando con la mia serie di articoli sulla transizione a macOS, vorrei parlare un po' della sandbox delle App di macOS.
È vivamente consigliato leggere prima l'articolo Struttura delle app macOS - darò per scontato che il lettore conosca la differenza tra App, processi (task), sappia qualcosa su launchd e la sua relazione con l'avvio delle App.
La prima volta che ho imparato a conoscere la sandbox di macOS, ho ingenuamente provato a creare una macro di Word.
Questo è (ancora) un vettore d'ingresso molto comune nell'ecosistema Windows, quindi volevo vedere se potevo semplicemente avviare processi e seminare il caos.
Beh, le cose non sono così facili su macOS - sono riuscito a eseguire processi, per esempio, ma sembrava che non potessero fare granché.
Il tentativo di creare file mi restituiva sempre un criptico errore Operation not permitted - cosa succede?
Ho iniziato a leggere un po' su macOS e Word e mi sono imbattuto in questo eccellente articolo di Adam Chester (lavora in MDSec). Consiglio vivamente di leggere l'articolo, ma riassumerò qui i risultati:
MacOS aveva un'utility funzionante chiamata sandbox-exec che eseguiva comandi in una sandbox. Sebbene deprecata, poteva mettere in luce parecchie cose. Nella sua pagina di manuale si vede che accetta un profile, quindi possiamo concludere che le regole della sandbox sono mantenute in profili. Questi profili possono avere varie forme - file, nomi predefiniti o persino stringhe letterali.
Le pagine di manuale affermano anche che gli sviluppatori dovrebbero usare la funzionalità App Sandbox. Leggendo di più ho capito che le regole della sandbox sono incorporate nel binario, nel nostro caso, situato in /Application/Microsoft Word.app/Contents/MacOS/Microsoft Word (se questo ti sembra estraneo, dai un'occhiata al mio articolo sulla struttura delle app macOS).
Anche se puoi estrarle facilmente a mano, è meglio usare uno strumento: 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
...
C'è molto da analizzare, quindi prendiamo alcune note di alto livello:
-dv, che sta per display e verbose. Poi, --entitlements presenta gli entitlements associati all'App o al binario (sì, codesign può funzionare su entrambi). Approfondiremo gli entitlements in un altro articolo, ma per ora diciamo che riflettono le capacità dell'App, e uno di essi afferma che l'App è in sandbox (com.apple.security.app-sandbox ha un valore booleano True).plist (di nuovo, nel mio [articolo sulla struttura delle app macOS]) potrebbero sospettare che il dizionario chiave-valore sia una rappresentazione di una property list, e avrebbero ragione.com.apple.security.temporary-exception.files.absolute-path.read-only menziona un array di percorsi assoluti da cui l'App può leggere.com.apple.security.temporary-exception.sbpl è qui - serve a creare quei famigerati file temporanei ~$whatever.docx a cui Word è così affezionato.Nell'articolo di MDSec del 2018 che ho menzionato prima, la parte deny file-write* sotto com.apple.security.temporary-exception.sbpl non esisteva, il che permetteva alle macro di creare file con contenuti arbitrari come /Library/LaunchAgents/~$evil.plist. Perché questo permette di evadere dalla sandbox?
LaunchAgents e LaunchDaemons sono un noto meccanismo (legittimo) di persistenza in macOS. Li ho già menzionati prima, ma puoi pensarli come Servizi (se vieni dal mondo Windows) - i LaunchDaemons si avviano all'avvio del sistema operativo (e quindi vivono al di fuori della sessione dell'utente), mentre i LaunchAgents si avviano quando un utente accede.
È interessante notare che entrambi sono descritti in semplici file plist. Ecco un esempio per il mio aggiornatore 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
}
Questi LaunchAgents e LaunchDaemons vengono avviati da launchd (ricordi quel processo?) e quindi sfuggono alla sandbox, poiché launchd non sapeva se la plist fosse stata creata da un processo in sandbox o meno (e anche se lo sapesse - come farebbe a sapere quali regole di sandbox applicare?).