Aller au contenu

PersistanceExécution

LaunchAgents et LaunchDaemons : persistance macOS

Les plists launchd des dossiers LaunchAgents et LaunchDaemons définissent ce que macOS lance au démarrage, à la connexion ou selon un planning.

Emplacement
~/Library/LaunchAgents, /Library/LaunchAgents, /Library/LaunchDaemons
Prouve
Quel code est configuré pour démarrer automatiquement, sous quel utilisateur, sur quel déclencheur, et depuis quand
Horodatages
Aucun horodatage interne ; dates de fichier APFS (nanosecondes, UTC)
Accès
Agents utilisateur : utilisateur propriétaire ; /Library : root ; accès complet au disque recommandé pour l'outil de collecte
Rétention
Jusqu'à la suppression du plist ; les overrides et les enregistrements BTM peuvent lui survivre
Collecte
Aftermath, mac_apt, UAC, Velociraptor

Ce que c'est

launchd est le PID 1 de macOS. Il démarre les démons système au boot et les agents par utilisateur à la connexion, à partir de définitions de tâches stockées sous forme de property lists. Un plist de tâche indique un Label, le programme à exécuter et les déclencheurs (au chargement, maintien en vie, intervalle, calendrier, modification de chemins). Les logiciels légitimes et la plupart des malwares macOS utilisent exactement le même mécanisme : c'est pourquoi ces dossiers sont le premier arrêt de toute revue de persistance.

Les démons s'exécutent dans le contexte système (root sauf si UserName en dispose autrement) sans utilisateur connecté. Les agents s'exécutent dans la session d'un utilisateur.

Où il se trouve

CheminTypeÉcrit parmacOS / protection
~/Library/LaunchAgentsAgent par utilisateurL'utilisateur, sans droits adminToutes versions
/Library/LaunchAgentsAgent pour tous les utilisateursAdministrateur (root)Toutes versions
/Library/LaunchDaemonsDémon systèmeAdministrateur (root)Toutes versions
/System/Library/LaunchAgents, /System/Library/LaunchDaemonsTâches AppleAppleVolume système scellé à partir de 11 Big Sur
/Library/Apple/System/Library/LaunchAgents, .../LaunchDaemonsTâches Apple sur le volume DataAppleProtégé par SIP
<App>.app/Contents/Library/LaunchAgents, .../LaunchDaemonsTâches embarquées dans l'application, enregistrées via SMAppServiceDéveloppeur13 Ventura et ultérieur
/private/var/db/com.apple.xpc.launchd/disabled.plist, disabled.<UID>.plistOverrides d'activation/désactivation launchdlaunchctl enable/disableroot
/private/var/db/com.apple.xpc.launchd/loginitems.<UID>.plistÉléments de connexion Service ManagementSMLoginItemSetEnabledroot
/Library/StartupItemsÉléments de démarrage historiquesAdministrateurMécanisme obsolète

Vérifiez aussi /var/root/Library/LaunchAgents et chaque répertoire personnel sous /Users.

Ce qu'il prouve

  • Qu'un programme a été configuré pour démarrer au boot, à la connexion, à intervalle régulier, selon un calendrier, sur modification de fichiers ou au montage d'un volume.
  • Le compte sous lequel s'exécute la tâche (un démon sans UserName signifie root).
  • Qu'écrire dans /Library/LaunchDaemons nécessitait root : un démon malveillant implique donc une élévation de privilèges.
  • Avec l'état d'exécution (launchctl print), si la tâche est chargée, son PID, son nombre d'exécutions et son dernier code de sortie.

Un plist seul ne prouve pas que la tâche s'est exécutée : elle peut être désactivée par un override, échouer au chargement ou pointer vers un binaire absent. Corroborez avec les journaux et les traces de processus.

Champs clés

D'après launchd.plist(5) :

CléSignification
LabelNom unique de la tâche (obligatoire)
Program / ProgramArgumentsExécutable et vecteur d'arguments
BundleProgramExécutable relatif à l'application, plists SMAppService uniquement
RunAtLoadDémarrage au chargement (boot ou connexion)
KeepAliveRedémarrage à la sortie, ou dictionnaire conditionnel
StartInterval / StartCalendarIntervalDémarrage périodique ou planifié
WatchPaths / QueueDirectories / StartOnMountDéclencheurs liés au système de fichiers
UserName / GroupNameIdentité d'exécution
EnvironmentVariablesEnvironnement (surveillez DYLD_INSERT_LIBRARIES)
StandardOutPath / StandardErrorPathFichiers de sortie, souvent des preuves utiles
LimitLoadToSessionTypeTypes de session pour les agents (par exemple Aqua)
AssociatedBundleIdentifiersApplication affichée pour cette tâche dans les réglages Ouverture
DisabledÉtat par défaut ; l'état launchctl enable/disable est conservé ailleurs

Horodatages

Les plists ne portent aucune date de création propre. Utilisez le système de fichiers : APFS stocke les dates de naissance, de modification, de changement et d'accès avec une précision à la nanoseconde, en UTC.

stat -f '%SB | %Sm | %N' -t '%Y-%m-%d %H:%M:%S' ~/Library/LaunchAgents/*.plist

La date de naissance est en général le meilleur repère de la date d'installation, mais elle peut être modifiée avec touch ou par des outils de copie. Vérifiez les dates du binaire référencé, FSEvents pour le dossier et l'enregistrement Background Task Management.

Rétention

Une tâche persiste jusqu'à la suppression de son plist. La suppression n'efface pas tout : les overrides disabled*.plist, les enregistrements Background Task Management, les entrées du unified log, FSEvents, les snapshots APFS locaux et les sauvegardes Time Machine peuvent conserver des traces.

Collecte

# Dead-box or live (root, Full Disk Access)
sudo ditto /Library/LaunchAgents ./case/la_system
sudo ditto /Library/LaunchDaemons ./case/ld_system
sudo ditto /private/var/db/com.apple.xpc.launchd ./case/launchd_db
for u in /Users/*; do [ -d "$u/Library/LaunchAgents" ] && \
  sudo ditto "$u/Library/LaunchAgents" "./case/la_$(basename "$u")"; done

# Live state (read-only)
sudo launchctl print system > ./case/launchctl_system.txt
launchctl print gui/501 > ./case/launchctl_gui501.txt
sudo launchctl print-disabled system > ./case/disabled_system.txt
  • UAC files/system/startup_items.yaml collecte les dossiers standard d'agents et de démons (par utilisateur, /Library, /System/Library et /Library/Apple/System/Library, mais pas /var/root ni les tâches intégrées aux applications), /Library/StartupItems et loginitems.*.plist ; live_response/system/launchctl.yaml exécute launchctl list.
  • Aftermath collecte les agents et démons parmi ses éléments de persistance.
  • N'exécutez jamais launchctl bootout, unload ou disable avant d'avoir tout documenté.

Analyse

  • plutil : plutil -p <file> affiche les plists XML ou binaires ; plutil -lint signale les plists malformés.
  • Plugin AUTOSTART de mac_apt : python mac_apt.py -o out E01 mac.E01 AUTOSTART.
  • Plugin launchd_plist de Plaso : log2timeline.py --parsers 'plist/launchd_plist' --storage-file ld.plaso ./case.
  • KnockKnock (objective-see/KnockKnock) énumère les éléments persistants d'un Mac live avec leur statut de signature.

Tri rapide du programme de chaque tâche :

for f in /Library/Launch*/*.plist ~/Library/LaunchAgents/*.plist; do
  printf '%s\t' "$f"; plutil -extract ProgramArguments json -o - "$f" 2>/dev/null \
    || plutil -extract Program raw -o - "$f"; echo
done

Conseils d'investigation

  • Un label com.apple.* en dehors de /System et /Library/Apple est un signal d'alerte : les tâches Apple ne se trouvent pas dans /Library/LaunchDaemons.
  • Quand ProgramArguments commence par /bin/sh, /bin/bash, /usr/bin/python3 ou /usr/bin/osascript, l'interpréteur est signé par Apple ; la charge utile est l'argument de script.
  • Calculez l'empreinte de chaque cible et vérifiez-la : codesign -dv --verbose=4, spctl --assess -vv, xattr -l pour la quarantaine.
  • Les cibles situées dans des dossiers cachés, /tmp, /Users/Shared ou portant des noms d'éditeur plausibles sous ~/Library/Application Support sont prioritaires.
  • Une tâche que launchctl print montre chargée mais sans plist sur disque signifie que le fichier a été supprimé après le chargement.
  • Comparez avec le master standard de l'organisation : outils de mise à jour, VPN, MDM et agents de sauvegarde constituent un bruit normal.
  • Examinez aussi cron et periodic et consultez le guide de la persistance launchd.

Voir aussi