
Approfondimento didattico sui bundle di app macOS, sui file plist e sul comportamento dei processi launchd, con note di sicurezza offensiva sull'impacchettamento di payload come file .app e sull'elusione del monitoraggio dell'albero dei processi.
Passare a macOS da Linux o Windows può sembrare come camminare in una terra nuova e sconosciuta. Dato che Linux è open-source e Windows è ben documentato e molto popolare (e macOS non è né l'uno né l'altro, per l'esattezza), macOS può essere impegnativo a volte. In questo articolo del blog, intendo discutere alcune delle prime cose che potresti notare su macOS - App, App ovunque!
Venendo da un background Windows o Linux, il concetto di App potrebbe sembrare strano. Sappiamo tutti che i thread sono "unità di esecuzione" e che i processi sono contenitori per thread con il proprio spazio di indirizzamento -- cosa c'è altro? Beh, i processi sono raramente distribuiti in file singoli. Sia su Windows che su Linux ci sono molte cose di cui il codice potrebbe aver bisogno per funzionare, alcune delle quali sono:
.dll, .so). Ad esempio, la libreria runtime del C (msvcr<version>.dll su Windows, libc-<version>.so) così come altre dipendenze.PE, che ha delle directory - una di queste è la directory delle risorse (anche in qualche modo documentata qui) che può contenere risorse (immagini, stringhe e altro). Le risorse potrebbero anche essere caricate dinamicamente dal disco, ovviamente.PE stesso (leggi qui) o nei file di catalogo (cioè, esternamente).xml, ini, json) e il Registro di Windows.Beh, macOS pone una forte enfasi sui Application Bundle. L'idea è di impacchettare (quasi) tutto ciò che serve al programma per funzionare in una struttura di directory - incluse risorse, informazioni di localizzazione, ecc.. Naturalmente, non tutto può essere impacchettato in modo ordinato (come la libreria runtime del C, per esempio) - ma significa comunque che le cose sono raggruppate insieme in modo pulito - nessun bisogno di navigare un enorme Registro o di leggere pagine di manuale per oscure posizioni di file di configurazione. Gli application bundle sono semplicemente directory che terminano con .app - anche se l'interfaccia utente nasconde l'estensione .app (e il fatto che sia una directory).
Dal punto di vista di un attaccante questo è interessante - poiché un Application Bundle può avere icone arbitrarie e nasconde l'estensione .app - consegnare malware potrebbe essere ottenuto ingannando un utente ignaro facendogli cliccare una tale app. Per esempio, pensa a un file Resume.app con un'icona PDF.
La struttura delle directory per un Application Bundle può essere facilmente esaminata, ovviamente con l'app Calculator integrata:
jbo@McJbo ~ % cd /System/Applications/Calculator.app
jbo@McJbo Calculator.app % ll
total 0
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 .
drwxr-xr-x 43 root wheel 1376 Mar 17 21:34 ..
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 Contents
jbo@McJbo Calculator.app % cd Contents
jbo@McJbo Contents % ll
total 16
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 .
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 ..
-rw-r--r-- 1 root wheel 2147 Mar 17 21:34 Info.plist
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 MacOS
-rw-r--r-- 204 root wheel 8 Mar 17 21:34 PkgInfo
drwxr-xr-x 4 root wheel 128 Mar 17 21:34 PlugIns
drwxr-xr-x 54 root wheel 1728 Mar 17 21:34 Resources
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 _CodeSignature
-rw-r--r-- 1 root wheel 461 Mar 17 21:34 version.plist
jbo@McJbo Contents % cd MacOS
jbo@McJbo MacOS % ll
total 344
drwxr-xr-x 3 root wheel 96 Mar 17 21:34 .
drwxr-xr-x 9 root wheel 288 Mar 17 21:34 ..
-rwxr-xr-x 1 root wheel 540912 Mar 17 21:34 Calculator
jbo@McJbo MacOS %
Come puoi vedere, Calculator.app è una directory. Sotto di essa c'è un singolo elemento - un'altra directory chiamata Contents.
Sotto Contents ci sono diversi elementi:
Info.plist - contiene metadati sull'App. Ne parleremo più avanti.MacOS - contiene l'eseguibile principale dell'App (come si può vedere nel terzo elenco di directory).PkgInfo - non obbligatorio. Un file binario che contiene informazioni sul pacchetto.PlugIns - non obbligatorio. Una directory che potrebbe contenere plugin per l'App. Calculator ne ha due - uno per "Base e Scientifica" e uno per "Esadecimale" (non so davvero perché abbiano fatto quella separazione, né mi interessa).Resources - non obbligatorio. Come suggerisce il nome, contiene risorse. Potresti trovare diversi elementi lì, inclusi file .icns con icone, oltre a directory con il suffisso .lprroj che sono relative alla localizzazione._CodeSignature - non obbligatorio. Come suggerisce il nome - contiene informazioni sulla firma del codice.version.plist - non obbligatorio, contiene informazioni sulla versione.Nota che ci sono pochissimi elementi che sono ufficialmente richiesti. In effetti, possiamo creare la nostra prima App senza nemmeno compilare nulla! Ma prima dobbiamo discutere di quel file Info.plist.
Più guardi macOS, più troverai questi strani file. Non sono altro che glorificati file di configurazione.
Avranno sempre un'estensione .plist, che è solo un modo abbreviato per chiamare il loro nome formale: file Property list.
Sfortunatamente, ci sono 3 diversi formati plist mantenuti da Apple:
xml, leggibile dagli esseri umani.json, non ampiamente utilizzato.bplist come magic.Fortunatamente, esiste un'utilità chiamata plutil che supporta tutti i formati. Per visualizzare un file plist, basta usare plutil -p. Per esempio:
jbo@McJbo Contents % plutil -p Info.plist | head -n 20
{
"BuildMachineOSBuild" => "22A380007"
"CFBundleDevelopmentRegion" => "English"
"CFBundleExecutable" => "Calculator"
"CFBundleGetInfoString" => "10.14, Copyright © 2000-2018, Apple Inc."
"CFBundleHelpBookFolder" => "Calculator.help"
"CFBundleHelpBookName" => "com.apple.Calculator.help"
"CFBundleIconFile" => "AppIcon"
"CFBundleIconName" => "AppIcon"
"CFBundleIdentifier" => "com.apple.calculator"
"CFBundleInfoDictionaryVersion" => "6.0"
"CFBundleName" => "Calculator"
"CFBundlePackageType" => "APPL"
"CFBundleShortVersionString" => "10.16"
"CFBundleSignature" => "????"
"CFBundleSupportedPlatforms" => [
0 => "MacOSX"
]
"CFBundleVersion" => "223"
"CTIgnoreUserFonts" => 1
jbo@McJbo Contents %
Ci sono anche funzionalità di conversione integrate in plutil - non le dimostreremo adesso.
Apple documenta diversi requisiti nel file Info.plist di un'App, ma ci sono pochissimi campi che sono effettivamente obbligatori. Ecco alcuni campi interessanti:
CFBundleExecutable - il nome dell'eseguibile principale, previsto sotto la directory MacOS.CFBundleIconFile - il nome del file dell'icona. Non obbligatorio.CFBundleIdentifier - un identificatore per l'App Bundle. Apple consiglia di usare una notazione DNS inversa (ad es. com.apple.calculator).CFBundleName - il nome del bundle.Con questo in mente, possiamo creare la nostra prima fantastica app, senza nemmeno programmare! Dai un'occhiata:
#!/bin/zsh
# Create Bundle structure
mkdir -p ./MyApp.app/Contents/MacOS
# Create main executable file - a shell script in our case
cat <<EOF > ./MyApp.app/Contents/MacOS/MyApp
#!/bin/zsh
osascript -e 'tell app "Finder" to display dialog "Hello from MyApp!"'
EOF
chmod +x ./MyApp.app/Contents/MacOS/MyApp
# Create the Info.plist file
cat <<EOF > ./MyApp.app/Contents/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>MyApp</string>
<key>CFBundleIdentifier</key>
<string>com.myapp</string>
<key>CFBundleName</key>
<string>MyApp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
</dict>
</plist>
EOF
Questo creerà una nuova app chiamata MyApp - facendo clic su di essa verrà semplicemente eseguito lo script zsh (chiamato MyApp). Nota che usa osascript, che è un interprete AppleScript e un intero vespaio, ma mostrerà solo un dialogo che dice Hello from MyApp!. Potresti scrivere codice arbitrario in quel file shell zsh, ovviamente. Le versioni più recenti di macOS potrebbero mostrare un prompt che chiede di consentire a zsh di chiamare osascript - discuteremo perché succede in un prossimo post, ma nota che non lo chiederà dopo una prima approvazione.
Avviare un'App significa che viene comunque creato un processo - ovviamente quello indicato da CFBundleExecutable. Sotto quale processo viene eseguita? Vediamo:
jbo@McJbo ~ % open -a Calculator
jbo@McJbo ~ % ps -A -j | grep Calculator | grep -v grep
jbo 12067 1 12067 0 1 S ?? 0:00.41 /System/Applications/Calculator.app/Contents/MacOS/Calculator
jbo@McJbo ~ %
Il comando open equivale a fare doppio clic sull'app Calculator - è piuttosto affascinante e ne discuteremo presto. Ora che Calculator è in esecuzione usiamo ps per mostrare i processi in esecuzione. Come previsto, /System/Applications/Calculator.app/Contents/MacOS/Calculator è il processo che viene eseguito e ha un PID di 12067. Tuttavia, il suo ID del processo padre è 1!
Il Process ID 1 su macOS è /sbin/launchd. È il "gestore di daemon/agenti a livello di sistema e per utente". Puoi immaginarlo come services.exe (se vieni da un background Windows) o systemd (se hai familiarità con Linux). Oltre a gestire i servizi (che su macOS sono chiamati Launch Agents e Launch Daemons), è anche il padre di tutte le Applicazioni, che è uno dei motivi per cui ottenere una struttura dei processi significativa su macOS è impegnativo.
Curiosamente, potresti comunque eseguire l'app Calculator semplicemente come processo invocando direttamente /System/Applications/Calculator.app/Contents/MacOS/Calculator, ma non sarà figlio di launchd:
jbo@McJbo ~ % /System/Applications/Calculator.app/Contents/MacOS/Calculator &
[1] 12502
jbo@McJbo ~ % 2023-04-04 15:54:22.987 Calculator[12502:1297087] XType: XTFontStaticRegistry is enabled by Info.plist.
jbo@McJbo ~ % ps -A -j | grep Calculator | grep -v grep
jbo 12502 951 12502 0 1 SN s000 0:00.31 /System/Applications/Calculator.app/Contents/MacOS/Calculator
jbo@McJbo ~ % echo $$
951
jbo@McJbo ~ %
In effetti, Calculator funziona senza problemi, ma ora è il processo figlio del nostro terminale. Una cosa da notare nel comportamento che abbiamo visto è che gli attaccanti potrebbero usarlo per scopi diversi. Ad esempio, gli attaccanti possono facilmente uscire dalla struttura dei processi per eludere gli strumenti di sicurezza, oltre ad abusare di vulnerabilità logiche (leggi il mio writeup sulla vulnerabilità di escape della sandbox macOS se hai tempo).
Ci sono altre responsabilità interessanti di launchd (leggi su LaunchAgents e LaunchDaemons) ma per ora non ne parleremo.
Qui ti ho mostrato un tipo di bundle ma ce ne sono molti altri (elenco non completo):
.app - abbiamo visto questo, sono Application Bundle che sono contenitori per App..framework - contiene Framework, che sono bundle caricabili. Sì, su macOS puoi chiamare dlopen su un file caricabile (.dylib) oppure caricare un intero bundle di framework (con risorse, codice, ecc.)..kext - contiene estensioni del kernel, che sono bundle caricabili ma per il kernel di macOS. Nelle versioni recenti del sistema operativo Apple cerca davvero di ridurre il numero di estensioni del kernel..plugin - come suggerisce il nome, un contenitore per plugin.Questo è il primo di una serie di brevi post del blog pensati per aiutare le persone a passare alla ricerca su macOS.
La prima cosa che ho notato, da una prospettiva di sicurezza offensiva, è quanto sia facile impacchettare payload in una bella struttura di app.
Le cose non sono così semplici però - nei prossimi un paio di post del blog scopriremo che ottenere l'esecuzione di codice non è così banale a causa delle molte funzionalità di sicurezza di macOS.
Resta sintonizzato!
Jonathan Bar Or (https://jonathanbaror.com)