LaunchAgents y LaunchDaemons: persistencia en macOS
Los plists de launchd en LaunchAgents y LaunchDaemons definen qué inicia macOS al arrancar, al iniciar sesión o con horario: el principal punto de persistencia.
- Ubicación
- ~/Library/LaunchAgents, /Library/LaunchAgents, /Library/LaunchDaemons
- Prueba
- Qué código está configurado para iniciarse automáticamente, con qué usuario, con qué disparador y desde cuándo
- Marcas de tiempo
- Sin marcas de tiempo internas; horas de archivo APFS (nanosegundos, UTC)
- Acceso
- Agentes de usuario: usuario propietario; /Library: root; se recomienda FDA para la herramienta de adquisición
- Retención
- Hasta que se borra el plist; las anulaciones y los registros BTM pueden sobrevivirle
- Adquisición
- Aftermath, mac_apt, UAC, Velociraptor
Herramientas
Comparar todas las herramientas- mac_aptCLI · código abierto
- PlasoCLI · código abierto
- KnockKnockGUI · código abierto
- plutilCLI · incluido en macOS
Qué es
launchd es el PID 1 de macOS. Inicia los daemons del sistema al arrancar y los agentes de cada usuario al iniciar sesión, a partir de definiciones de trabajos guardadas como property lists. Un plist de trabajo indica un Label, el programa que se ejecuta y los disparadores (al cargar, mantener vivo, por intervalo, por calendario, por cambios en rutas de archivos). El software legítimo y la mayor parte del malware para macOS usan exactamente el mismo mecanismo, por eso estas carpetas son la primera parada en cualquier revisión de persistencia.
Los daemons se ejecutan en el contexto del sistema (como root salvo que UserName indique otra cosa) sin necesidad de un usuario con sesión iniciada. Los agentes se ejecutan dentro de la sesión de un usuario.
Dónde se encuentra
| Ruta | Tipo | Escrito por | macOS / protección |
|---|---|---|---|
~/Library/LaunchAgents | Agente por usuario | El propio usuario, sin necesidad de administrador | Todas las versiones |
/Library/LaunchAgents | Agente para todos los usuarios | Administrador (root) | Todas las versiones |
/Library/LaunchDaemons | Daemon del sistema | Administrador (root) | Todas las versiones |
/System/Library/LaunchAgents, /System/Library/LaunchDaemons | Trabajos de Apple | Apple | Volumen de sistema sellado desde 11 Big Sur |
/Library/Apple/System/Library/LaunchAgents, .../LaunchDaemons | Trabajos de Apple en el volumen Data | Apple | Protegido por SIP |
<App>.app/Contents/Library/LaunchAgents, .../LaunchDaemons | Trabajos incluidos en la app y registrados con SMAppService | Desarrollador | 13 Ventura+ |
/private/var/db/com.apple.xpc.launchd/disabled.plist, disabled.<UID>.plist | Anulaciones de activación/desactivación de launchd | launchctl enable/disable | root |
/private/var/db/com.apple.xpc.launchd/loginitems.<UID>.plist | Elementos de inicio de Service Management | SMLoginItemSetEnabled | root |
/Library/StartupItems | Elementos de arranque heredados | Administrador | Mecanismo obsoleto |
Revisa también /var/root/Library/LaunchAgents y cada carpeta personal de /Users.
Qué prueba
- Que un programa se configuró para iniciarse al arrancar, al iniciar sesión, por intervalo, según un calendario, ante cambios de archivos o al montar un volumen.
- La cuenta con la que se ejecuta el trabajo (un daemon sin
UserNamese ejecuta como root). - Que escribir en
/Library/LaunchDaemonsrequería root, de modo que un daemon malicioso implica una escalada de privilegios. - Con el estado en ejecución (
launchctl print), si el trabajo está cargado, su PID, el número de ejecuciones y el último código de salida.
Un plist por sí solo no prueba que el trabajo llegara a ejecutarse: puede estar desactivado por una anulación, fallar al cargarse o apuntar a un binario inexistente. Corrobóralo con logs y evidencias de procesos.
Campos clave
Según launchd.plist(5):
| Clave | Significado |
|---|---|
Label | Nombre único del trabajo (obligatorio) |
Program / ProgramArguments | Ejecutable y vector de argumentos |
BundleProgram | Ejecutable relativo a la app, solo en plists de SMAppService |
RunAtLoad | Iniciar al cargarse (arranque o inicio de sesión) |
KeepAlive | Reiniciar al salir, o diccionario condicional |
StartInterval / StartCalendarInterval | Inicio periódico o programado |
WatchPaths / QueueDirectories / StartOnMount | Disparadores del sistema de archivos |
UserName / GroupName | Identidad de ejecución |
EnvironmentVariables | Entorno (atención a DYLD_INSERT_LIBRARIES) |
StandardOutPath / StandardErrorPath | Archivos de salida, a menudo evidencia útil |
LimitLoadToSessionType | Tipos de sesión para agentes (p. ej. Aqua) |
AssociatedBundleIdentifiers | App que se muestra para este trabajo en los ajustes de Ítems de inicio |
Disabled | Estado por defecto; el estado de launchctl enable/disable se guarda aparte |
Marcas de tiempo
Los plists no llevan fecha de creación propia. Usa el sistema de archivos: APFS almacena las horas de creación, modificación, cambio y acceso con precisión de nanosegundos en UTC.
stat -f '%SB | %Sm | %N' -t '%Y-%m-%d %H:%M:%S' ~/Library/LaunchAgents/*.plist
La hora de creación suele ser la mejor referencia de "instalado el", pero puede alterarse con touch o con herramientas de copia. Revisa las horas del binario referenciado, FSEvents para la carpeta y el registro de Background Task Management.
Retención
Un trabajo persiste hasta que se elimina su plist. La eliminación no lo borra todo: las anulaciones disabled*.plist, los registros de Background Task Management, las entradas del unified log, FSEvents, los snapshots locales de APFS y las copias de Time Machine pueden conservar rastros.
Adquisición
# 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.yamlrecopila las carpetas estándar de agentes y daemons (por usuario,/Library,/System/Libraryy/Library/Apple/System/Library, pero no/var/rootni los trabajos integrados en las apps),/Library/StartupItemsyloginitems.*.plist;live_response/system/launchctl.yamlejecutalaunchctl list. - Aftermath recopila los launch agents y daemons entre sus elementos de persistencia.
- No ejecutes nunca
launchctl bootout,unloadnidisableantes de documentar.
Análisis
- plutil:
plutil -p <file>muestra plists XML o binarios;plutil -lintseñala los que están mal formados. - Plugin
AUTOSTARTde mac_apt:python mac_apt.py -o out E01 mac.E01 AUTOSTART. - Plugin
launchd_plistde Plaso:log2timeline.py --parsers 'plist/launchd_plist' --storage-file ld.plaso ./case. - KnockKnock (objective-see/KnockKnock) enumera los elementos persistentes de un Mac en vivo con su estado de firma.
Triaje rápido del programa de cada trabajo:
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
Consejos para la investigación
- Una etiqueta
com.apple.*fuera de/Systemy/Library/Applees una señal de alarma: los trabajos de Apple no residen en/Library/LaunchDaemons. - Cuando
ProgramArgumentsempieza por/bin/sh,/bin/bash,/usr/bin/python3o/usr/bin/osascript, el intérprete está firmado por Apple; la carga útil es el script pasado como argumento. - Calcula el hash y revisa cada destino:
codesign -dv --verbose=4,spctl --assess -vv,xattr -lpara la cuarentena. - Los destinos en carpetas ocultas,
/tmp,/Users/Sharedo con nombres que imitan a fabricantes bajo~/Library/Application Supportmerecen prioridad. - Un trabajo que
launchctl printmuestra como cargado pero sin plist en disco significa que el archivo se eliminó después de cargarse. - Compara con la configuración estándar de la organización: actualizadores, VPN, MDM y agentes de copia de seguridad son ruido normal.
- Revisa también cron y periodic y consulta la guía de persistencia en launchd.