macOS Persistence Cheatsheet

v1.0 (07/04/2026)

DFIR artifacts, artifact scope, macOS version applicability, minimum privilege required to establish or modify persistence, false-positive risk, baseline dependence, and review guidance.

Use search to filter the sheet, click the legend badges to narrow the results, and switch density when you want a tighter view. Scope refers to where the artifact lives; Privilege refers to the access needed to put it in place or change it.
All mechanisms visible
Density
View
Controls hidden
Core launch Shell & scheduled Extensions & handlers Adjacent & legacy Profiles & MDM Checklists & logs
Scope
Source of Truth
Signal
Required Privilege
False Positive Risk
Overview
Mechanism scan
Quick index of the persistence mechanisms covered below. The search box and legend filters apply here too, so this view stays in sync with the tables.
31 visible
31 total mechanisms
01 Core Launch & Login Persistence
MechanismRequired privilegeSource of TruthSignalCollection / TriageTriggerWhat to review
Launch Agents [S1]
Mixed10.4+
Core current
Mixed
BothHigh
Artifact / Path
/System/Library/LaunchAgents/Library/LaunchAgents~/Library/LaunchAgents
Resolves to
Program / ProgramArguments[0]Referenced MachServices / Sockets if present
Enumerate
launchctl print gui/<uid>find ~/Library/LaunchAgents /Library/LaunchAgents -maxdepth 1 -name '*.plist'
Inspect
launchctl print gui/<uid>/<label>launchctl blame gui/<uid>/<label>plutil -p <plist>
User login / GUI session
Medium FP · Baseline: Critical · Use: Autostart
Parse Label, Program, ProgramArguments, RunAtLoad, KeepAlive, WatchPaths, QueueDirectories, StartCalendarInterval, PathState, Sockets, and MachServices. Use launchctl blame to identify the actual trigger that loaded a service: this distinguishes a job started by RunAtLoad from one started by WatchPaths, Sockets, or MachServices, which matters for hunting on-demand persistence. Prioritize targets outside Apple or vendor-controlled paths and jobs present on disk but absent or odd in live launchctl state. Scope and required privilege vary by path: user-scoped in ~/Library, system-scoped in /Library.
Launch Daemons [S2]
SystemAll
Core current
Root
BothHigh
Artifact / Path
/System/Library/LaunchDaemons/Library/LaunchDaemons
Resolves to
Program / ProgramArguments[0]Referenced service endpoints and loaded binary path
Enumerate
launchctl print systemfind /Library/LaunchDaemons -maxdepth 1 -name '*.plist'
Inspect
launchctl print system/<label>launchctl blame system/<label>plutil -p <plist>
Boot / pre-login
Low FP · Baseline: Critical · Use: Autostart
Very high-value persistence. Review launchd keys such as RunAtLoad, KeepAlive, StartCalendarInterval, and PathState, plus ownership, signer, Team ID, and whether the target binary lives in a user-writable or otherwise weak path. Use launchctl blame on the live service-target to surface the real trigger (RunAtLoad vs Sockets / MachServices / WatchPaths) instead of relying on plist intent alone.
launchd overrides / disabled state [S3]
MixedAll
Storage varies
Mixed
StateMedium
Artifact / Path
Persistent disabled / override state
Resolves to
Effective enabled / disabled state for a labelFinal live state shown by launchctl, not just plist intent
Enumerate
launchctl print-disabled systemlaunchctl print-disabled gui/<uid>
Inspect
launchctl print system/<label>launchctl print gui/<uid>/<label>
Persistent state across reboot
Medium FP · Baseline: Helpful · Use: State control
All versions; storage details vary. Do not trust plist review alone: disabled or override state can persist independently of the plist, and the exact storage location has changed over time.
BTM / SMAppService [S4]
Mixed13+
Ventura+ / Background Task Management
Mixed
BothMedium
Artifact / Path
~/Library/Application Support/com.apple.backgroundtaskmanagementagent/backgrounditems.btm (legacy / pre-Ventura)/private/var/db/com.apple.backgroundtaskmanagement/BackgroundItems-v*.btm (Ventura+, versioned BTM store)
Resolves to
Registered helper binary or app bundle in BTMOwning app bundle, Team ID, and registration record
Enumerate
sfltool dumpbtm
Inspect
plutil -p ~/Library/Application\ Support/com.apple.backgroundtaskmanagementagent/backgrounditems.btmcodesign -dv --verbose=4 <helper_or_owner_app>
User login / helper startup
Medium FP · Baseline: Critical · Use: Login helper
macOS 13+ Background Task Management / SMAppService model. Review BTM state and on-disk records together, then correlate each entry with the owning app bundle, signer, Team ID, and the helper actually present on disk. Treated here as mixed scope because modern SMAppService can register login items as well as launch-item style services.
Login Items (legacy / classic) [S4a]
UserLegacy / pre-13
Pre-BTM login items
User
BothMedium
Artifact / Path
Legacy login item records / shared file list stateResolved app or helper path launched at user login
Resolves to
App bundle or helper registered for loginOwning app, signer, Team ID, and user-scoped registration
Enumerate
osascript -e 'tell application "System Events" to get the properties of every login item'plutil -p <legacy_login_item_store_when_present>
Inspect
codesign -dv --verbose=4 <login_item_or_owner_app>ls -la <resolved_login_item_path>
User login
Medium FP · Baseline: Critical · Use: Login helper
Legacy / classic login items predate BTM / SMAppService. Backing files and exact storage vary by OS generation, so verify on the target OS and map each registration to the real app or helper on disk.
Embedded Login Helpers [S5]
User10.6.6+
superseded by SMAppService on 13+
User
BothNoisy
Artifact / Path
<App>.app/Contents/Library/LoginItems
Resolves to
Embedded helper app / binaryOwner app bundle, signer, Team ID, and expected install location
Enumerate
find /Applications ~/Applications -path '*/Contents/Library/LoginItems/*'
Inspect
codesign -dv --verbose=4 <helper>plutil -p <owner_app>/Contents/Info.plist
Login / app-managed helper start
High FP · Baseline: Critical · Use: Helper launch
Common in legitimate software too. Treat as modern app-bundled persistence and baseline Bundle ID, Team ID, signer, and owner app. Focus on signer mismatches, stale paths, and helpers embedded in unusual app locations.
02 Shell, Access, and Scheduled Execution
MechanismRequired privilegeSource of TruthSignalCollection / TriageTriggerWhat to review
Shell init (zsh) [S7]
MixedCurrent default
Default on current macOS
Mixed
DiskMedium
Artifact / Path
/etc/zshenv/etc/zprofile/etc/zshrc/etc/zlogin/etc/zlogout~/.zshenv~/.zprofile~/.zshrc~/.zlogin~/.zlogout
Resolves to
Commands sourced by zsh startup filesZDOTDIR-resolved files and downstream scripts / binaries
Enumerate
echo $ZDOTDIRls -la ~/.z* /etc/z*
Inspect
grep -nEv '^\s*(#|$)' ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin ~/.zlogout /etc/zshenv /etc/zprofile /etc/zshrc /etc/zlogin /etc/zlogout
Login shell / interactive shell / logout
High FP · Baseline: Helpful · Use: Shell exec
zsh is the default shell on current macOS, but not exclusive to a single release boundary. Resolve ZDOTDIR before concluding nothing changed, or you can miss relocated per-user dotfiles and hidden sourced files.
Shell init (bash / sh) [S8]
MixedStill present
Common on older / migrated profiles
Mixed
DiskMedium
Artifact / Path
/etc/profile/etc/bashrc~/.bash_profile~/.bash_login~/.bashrc~/.bash_logout~/.profile
Resolves to
Commands sourced by bash / sh startup filesReferenced scripts, PATH entries, and ENV / BASH_ENV targets
Enumerate
env | grep -E 'PATH|ENV|BASH_ENV'ls -la ~/.bash* ~/.profile /etc/profile /etc/bashrc
Inspect
grep -nEv '^\s*(#|$)' ~/.bash_profile ~/.bash_login ~/.bashrc ~/.bash_logout ~/.profile /etc/profile /etc/bashrc
Login shell / interactive shell
High FP · Baseline: Helpful · Use: Shell exec
bash remains present and is still common on older, migrated, admin, CI, and developer estates. Look for hidden sourcing, PATH abuse, curl or osascript launchers, and suspicious environment exports.
SSH authorized_keys [S9]
MixedAll
Access persistence
Mixed
DiskHigh
Artifact / Path
~/.ssh/authorized_keys~/.ssh/config/etc/ssh/sshd_config
Resolves to
Authorized key entries actually accepted by sshdAuthorizedKeysFile path(s), command= restrictions, principals, and signer context
Enumerate
grep -i '^AuthorizedKeysFile' /etc/ssh/sshd_configls -la ~/.ssh
Inspect
ssh-keygen -lf ~/.ssh/authorized_keysgrep -n '' ~/.ssh/authorized_keys /etc/ssh/sshd_config
SSH connection
Medium FP · Baseline: Helpful · Use: Access persistence
Access persistence rather than local autostart. High signal on privileged or shared admin accounts. Also verify AuthorizedKeysFile in sshd_config so you do not miss a custom key path. Treated as mixed scope because key material is commonly user-scoped while AuthorizedKeysFile policy may be system-scoped.
Cron [S10]
MixedAll
Less native
Mixed
DiskMedium
Artifact / Path
/etc/crontab/usr/lib/cron/tabs/
Resolves to
Command field and invoked script pathPer-user or system crontab entry actually executed
Enumerate
crontab -lsudo ls -la /usr/lib/cron/tabs/ /etc/crontabfor u in $(dscl . list /Users | grep -v '^_'); do sudo crontab -u "$u" -l 2>/dev/null && echo "[$u]"; done
Inspect
sudo grep -nEv '^\s*(#|$)' /etc/crontab /usr/lib/cron/tabs/*
Schedule / periodic
Medium FP · Baseline: Helpful · Use: Scheduled exec
More common on older or mixed estates than on launchd-first modern deployments. macOS-specific quirk: per-user crontabs live in /usr/lib/cron/tabs/, not /var/cron/tabs as on most BSD/Linux references. crontab -l only shows the current user's jobs, so iterate over local accounts (or list the spool directly with root) to avoid missing entries. Verify targets for user-writable paths, temp directories, or masquerading names.
At / atrun [S11]
MixedAll
Rare; atrun system daemon, per-user job queues
Mixed
BothContext
Artifact / Path
at queue / spoolat.allow / at.deny
Resolves to
Queued command / script in the at jobatrun-driven one-shot execution target
Enumerate
atq
Inspect
at -c <job>grep -n '' /etc/at.allow /etc/at.deny
One-shot deferred execution
Low FP · Baseline: Optional · Use: Scheduled exec
Largely theoretical on default macOS: /usr/libexec/atrun is disabled out of the box, so submitted at jobs do not actually fire until an attacker (or admin) enables it with root via sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist. Treat any host where atrun is loaded and at jobs are queued as a strong, version-independent signal. Prefer command-based review (atq, at -c <job>) over a single hardcoded spool path and focus on jobs that launch from temp, shared, hidden, or user-writable locations.
Periodic Jobs [S12]
SystemAll
System scheduler
Root
DiskContext
Artifact / Path
/etc/periodic/daily/etc/periodic/weekly/etc/periodic/monthly/etc/defaults/periodic.conf/etc/periodic.conf (override; absent by default)
Resolves to
Script executed from periodic dirsAny custom path introduced through periodic.conf
Enumerate
ls -la /etc/periodic/daily /etc/periodic/weekly /etc/periodic/monthly
Inspect
grep -R '.' /etc/periodic /etc/defaults/periodic.conf /etc/periodic.conf 2>/dev/null
Daily / weekly / monthly maintenance
Low FP · Baseline: Helpful · Use: Scheduled exec
Low-frequency but worth keeping for exhaustive modern macOS triage. Note that /etc/defaults/periodic.conf is the shipped default; /etc/periodic.conf does not exist by default and is only read if created as a local override, so its mere presence is itself a signal worth investigating. Review inherited script paths, shellouts, and permissions rather than assuming stock maintenance content.
Emond [S13]
System≤12; removed in 13
Removed in Ventura
Root
DiskHigh
Artifact / Path
/etc/emond.d/rules//private/var/db/emondClients/System/Library/LaunchDaemons/com.apple.emond.plist
Resolves to
Action command or script referenced by the ruleCondition-to-action chain that leads to code execution
Enumerate
ls -la /etc/emond.d/rules /private/var/db/emondClients
Inspect
plutil -p /System/Library/LaunchDaemons/com.apple.emond.plistgrep -R '.' /etc/emond.d/rules
Event-driven (startup, auth, policy)
Low FP · Baseline: Optional · Use: Event-driven
Apple removed emond entirely in macOS 13 Ventura: the /sbin/emond binary and /System/Library/LaunchDaemons/com.apple.emond.plist no longer ship. On any modern fleet (13+), the presence of /etc/emond.d/rules/*, /private/var/db/emondClients, or any emond-related plist is itself a strong signal because the mechanism does not exist natively. On ≤12 hosts, treat unexplained rules, clients, or odd actions as immediate priority.
03 Extensions, Browser, Interpreter, and Handler Surfaces
MechanismRequired privilegeSource of TruthSignalCollection / TriageTriggerWhat to review
System Extensions [S14]
System10.15+
Current ext model
AdminApproval
StateMedium
Artifact / Path
Contents/Library/SystemExtensions
Owning app bundleCurrent activation state
Resolves to
Activated system extension and owning appBundle ID, Team ID, and activation state shown by systemextensionsctl
Enumerate
systemextensionsctl list
Inspect
codesign -dv --verbose=4 <owning_app>systemextensionsctl list | grep -E 'enabled|active'
Boot / activation / app-managed load
Medium FP · Baseline: Critical · Use: Extension / plugin
macOS 10.15+ current model. Use state-first enumeration. Baseline Team ID, Bundle ID, and activation status, then verify the owning app, signer, and approval context.
KEXTs [S15]
SystemLegacy; deprecated 10.15+, tighter on 11+
Superseded by System Extensions
RootApproval
BothHigh
Artifact / Path
/Library/Extensions
Resolves to
Loaded kext bundle ID and pathActual loaded extension plus the on-disk bundle
Enumerate
kmutil showloadedkextstat
Inspect
codesign -dv --verbose=4 <kext>kmutil inspect -b <bundle_id>
Boot / extension load
Low FP · Baseline: Critical · Use: Extension / plugin
Legacy mechanism. Kernel extensions are deprecated from macOS 10.15 onward in favor of system extensions, and their use is more restricted on 11+. Presence on a modern fleet is high-signal and often implies exception or reduced-security context.
Chromium Extensions [S16]
MixedAll
Chromium only
Mixed
BothNoisy
Artifact / Path
~/Library/Application Support/Google/Chrome/*/Extensions~/Library/Application Support/Google/Chrome/*/Preferences/Library/Managed Preferences/com.google.Chrome.plist
Resolves to
Extension ID, manifest, and effective policy sourceForce-installed or managed extension config tied to a real profile
Enumerate
find ~/Library/Application\ Support/Google/Chrome -path '*/Extensions/*'plutil -p /Library/Managed\ Preferences/com.google.Chrome.plist 2>/dev/null | grep -E 'ExtensionInstallForcelist|ExtensionSettings'
Inspect
grep -n 'extensions' ~/Library/Application\ Support/Google/Chrome/*/Preferencescodesign -dv --verbose=4 <extension_host_app_if_app_mode>
Browser launch / profile load
High FP · Baseline: Critical · Use: Extension / plugin
All versions with Chromium-based browsers; noisy on managed fleets. Review extension IDs, policy-forced installs, managed profiles, update URLs, and any extension granted unusual permissions or loaded from a nonstandard path. Treated as mixed scope because this row covers both per-profile installs and managed / policy-backed installs.
Safari App Extensions [S17]
MixedSafari 10 / 10.12+
Safari 10+ app extension model
UserApproval
BothMedium
Artifact / Path
Owning app bundle / appexSafari extension state / preferences
Resolves to
appex / owner app bundle actually providing the extensionStored extension state tied to the installed owner app
Enumerate
pluginkit -mAv | grep -i safarifind /Applications ~/Applications -name '*.appex'
Inspect
codesign -dv --verbose=4 <appex_or_owner_app>find ~/Library/Containers/com.apple.Safari/Data/Library/Preferences -name "*.plist" -exec plutil -p {} \; 2>/dev/null
Browser launch / extension activation
Medium FP · Baseline: Helpful · Use: Extension / plugin
Safari App Extensions are the older app/appex-backed model, introduced with Safari 10 (macOS 10.12 Sierra; also available as a Safari 10 update on 10.11.6 El Capitan). Review the owner app, signer, Team ID, and whether stored state matches the app actually present on disk. Treated as mixed scope because extension state is user-specific while the owning app may live in either user or global application space.
Safari Web Extensions [S17a]
MixedSafari 14+
Current Safari web extension model
UserApproval
BothMedium
Artifact / Path
Owning app bundle / web extension payloadSafari extension state / preferences
Resolves to
Safari web extension actually providing the browser codeStored enablement / permissions tied to the installed owner app
Enumerate
pluginkit -mAv | grep -i safarifind /Applications ~/Applications -name '*.appex'
Inspect
codesign -dv --verbose=4 <appex_or_owner_app>find ~/Library/Containers/com.apple.Safari/Data/Library/Preferences -name "*.plist" -exec plutil -p {} \; 2>/dev/null
Browser launch / extension activation
Medium FP · Baseline: Helpful · Use: Extension / plugin
Safari Web Extensions are the current model on Safari 14+. Review the owner app, signer, Team ID, extension permissions, and whether the stored state matches the installed app and expected browser profile. Treated as mixed scope because extension state is user-specific while the owning app may live in either user or global application space.
Application / daemon plug-ins [S17b]
MixedAll
Stealthy host-loaded plug-ins
Mixed
BothContext
Artifact / Path
App / daemon-specific plug-in directories/Library/Security/SecurityAgentPlugins/ (authorization plug-ins)/Library/DirectoryServices/PlugIns/ (Open Directory)~/Library/iTunes/iTunes Plug-ins (historical; iTunes removed in 10.15)~/Library/QuickLook / /Library/QuickLook/Library/Spotlight
Resolves to
Actual plug-in bundle loaded by the host app or daemonOwning host, signer, and execution context inherited from that host
Enumerate
pluginkit -mAvqlmanage -m plugins 2>/dev/nullmdimport -L 2>/dev/null
Inspect
codesign -dv --verbose=4 <plugin_bundle>find ~/Library /Library -type d \( -name '*Plug-Ins*' -o -name 'QuickLook' -o -name 'Spotlight' \) 2>/dev/null
Host app / daemon launch or plug-in discovery
Medium FP · Baseline: Helpful · Use: Extension / plugin
Covers non-browser plug-in persistence such as app-specific plug-ins and daemon plug-ins. Two especially sensitive subclasses deserve dedicated attention: authorization plug-ins in /Library/Security/SecurityAgentPlugins/ (loaded by authorizationhost / SecurityAgent, can intercept credential prompts and run very early in the auth chain) and Open Directory plug-ins in /Library/DirectoryServices/PlugIns/ (loaded by opendirectoryd, root-context, very high impact). For all rows, focus on writable plug-in directories, unexpected host / signer pairings, and bundles that inherit sensitive host privileges or sandbox exceptions. Treated as mixed scope because plug-ins may live in user-writable or system-wide host directories.
Python Startup Hooks [S18]
MixedAll
Dev-heavy
Mixed
DiskContext
Artifact / Path
.pthsitecustomize.pyusercustomize.py
Resolves to
Imported module or code run on interpreter start.pth executable lines, sitecustomize, or usercustomize targets
Enumerate
python3 -m sitefind ~/Library /usr/local /opt/homebrew \( -name '*.pth' -o -name 'sitecustomize.py' -o -name 'usercustomize.py' \) 2>/dev/null
Inspect
grep -n '' <.pth_or_customize_file>
Python interpreter start
High FP · Baseline: Helpful · Use: Interpreter hook
Most relevant on dev, automation, and build hosts. Hunt for executable import lines in .pth files, unexpected site-package directories, and hooks that silently reach out to the network or launch other interpreters. Treated as mixed scope because this row covers both user-specific and system-level Python locations.
LaunchServices / URL Handlers [S19]
UserAll
Indirect / stateful
User
BothContext
Artifact / Path
LSHandlersCustom scheme / file-handler associations~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist
Resolves to
Handler Bundle ID and actual registered appCustom scheme or file association that causes the execution path
Enumerate
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -dump
Inspect
plutil -p ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist
File open / URL scheme invocation
High FP · Baseline: Helpful · Use: Handler hijack
All versions; preference and registered-state storage varies. Lower-priority and indirect, but useful when persistence appears to hinge on a document opener or custom URL scheme workflow.
04 Persistence-Adjacent & Legacy Mechanisms
These stay in the sheet because they matter in DFIR, but they are not primary autostart mechanisms in the same sense as launchd, BTM, login helpers, or shell startup.
MechanismRequired privilegeSource of TruthSignalCollection / TriageTriggerWhat to review
Installer Packages [S20]
MixedAll
Dropper-adjacent
Admin
DiskContext
Artifact / Path
preinstallpostinstall
Resolves to
preinstall / postinstall script targetDropped payload or persistence artifact created by the package
Enumerate
pkgutil --pkgs
Inspect
pkgutil --expand <pkg> <dir>spctl --assess --type install <pkg>
Package installation
High FP · Baseline: Helpful · Use: Installer / dropper
Usually a deployment or dropper surface rather than final persistence. Review because it often lays down LaunchDaemons, helpers, login items, or extensions. Scope is treated as mixed because packages are a deployment surface that can lay down either user- or system-scoped persistence.
DYLD_* injection / LSEnvironment [S20a]
MixedAll; constrained on modern macOS
Dynamic library environment injection
Mixed
DiskMedium
Artifact / Path
LSEnvironment in app Info.plistEnvironmentVariables in launchd plistsDYLD_INSERT_LIBRARIESDYLD_FRAMEWORK_PATH
Resolves to
Injected dylib path and targeted host processPlist or app bundle whose launch path causes the dylib to load
Enumerate
grep -R -n 'DYLD_\|LSEnvironment\|EnvironmentVariables' /Applications ~/Applications ~/Library/LaunchAgents /Library/LaunchAgents /Library/LaunchDaemons 2>/dev/null
Inspect
plutil -p <plist_or_Info.plist>codesign -d --entitlements :- <target_app_or_binary> 2>/dev/null
Target process launch
Medium FP · Baseline: Helpful · Use: Execution hijack
Separate from Mach-O load-command tampering. On modern macOS, the dynamic loader ignores DYLD_* environment variables for Apple platform binaries and for third-party apps built with the hardened runtime, unless the target carries one of two specific entitlements: com.apple.security.cs.allow-dyld-environment-variables or com.apple.security.cs.disable-library-validation. The fastest discriminating check is therefore codesign -d --entitlements :- <app> on the target host process: a hardened-runtime app exposing either entitlement is the realistic injection target (this is the class of weakness behind the Zoom dylib injection cases). Treated as mixed scope because the persistence artifact may be a user app / plist or a system-wide app / launch item.
Application / binary modification [S20b]
MixedAll
Host app subversion / infected binary
Mixed
DiskMedium
Artifact / Path
Modified app bundle executable or resourcesTrojanized or infected Mach-O host binary
Resolves to
Host app or binary that now re-executes malicious codeTampered code signature, loader chain, or embedded persistence logic
Enumerate
codesign --verify --deep --strict <app_or_binary>spctl --assess --type execute <app_or_binary>
Inspect
codesign -dv --verbose=4 <app_or_binary>otool -l <app_or_binary>shasum -a 256 <app_or_binary>
Host app / binary execution
Medium FP · Baseline: Helpful · Use: Execution hijack
Broader than added load commands alone. Use this row for trojanized apps, patched bundle contents, and infected binaries that silently restore or re-establish other persistence when the host is launched. Treated as mixed scope because the tampered host may live in user-writable or system-wide locations.
LC_LOAD_DYLIB Addition [S21]
MixedAll
Binary-level
Mixed
DiskMedium
Artifact / Path
Mach-O load commands
Resolves to
Injected / added dylib pathImpacted Mach-O binary that loads the dylib at runtime
Enumerate
otool -L <binary>
Inspect
otool -l <binary>codesign -dv <binary>
Trusted binary execution
Medium FP · Baseline: Helpful · Use: Execution hijack
Persistence-adjacent binary tampering. Focus on unexpected dylibs from user-writable paths, hidden directories, or signers that do not fit the host baseline. Treated as mixed scope because the modified host binary may live in user-writable or system-wide locations.
Dylib Hijacking / Proxying [S21a]
MixedAll; constrained on hardened runtime
Vulnerable search paths and LC_REEXPORT_DYLIB proxies
Mixed
DiskMedium
Artifact / Path
App-local Frameworks / PlugIns / LoginItems dylib search pathsWeak / optional dylib dependencies declared by a host Mach-OProxy dylib carrying an LC_REEXPORT_DYLIB to a renamed copy of the original
Resolves to
Malicious dylib loaded under an existing trusted host processOriginal library functionality preserved through the proxy re-export
Enumerate
otool -l <host_binary> | grep -E 'LC_LOAD_DYLIB|LC_LOAD_WEAK_DYLIB|LC_REEXPORT_DYLIB' -A 3find <App>.app/Contents \( -name '*.dylib' -o -name '*.framework' \)
Inspect
otool -l <suspect_dylib> | grep -A 3 LC_REEXPORT_DYLIBcodesign -dv --verbose=4 <suspect_dylib>codesign -d --entitlements :- <host_app> 2>/dev/null
Host app launch / dynamic load
Medium FP · Baseline: Helpful · Use: Execution hijack
Distinct from a simple LC_LOAD_DYLIB addition. Two related techniques: (1) dylib hijacking exploits a host that searches multiple directories for a dylib (or has a weak / optional dependency that does not exist), letting the attacker drop a malicious dylib of the same name in the first searched / writable location; (2) dylib proxying replaces a real dependency with a malicious dylib that carries an LC_REEXPORT_DYLIB command pointing to a renamed copy of the original, so functionality is preserved while the proxy executes its constructor inside the trusted host. Both inherit the host's TCC permissions (camera, mic, etc.). Detection pivots: presence of LC_REEXPORT_DYLIB in an app's bundled dylib, dylibs in writable per-app directories whose signer differs from the host, and hardened-runtime hosts carrying com.apple.security.cs.disable-library-validation.
Login Hooks [S22]
MixedDeprecated since 10.11
User or system plist; verify on target OS
Mixed
DiskHigh
Artifact / Path
/Library/Preferences/com.apple.loginwindow.plist~/Library/Preferences/com.apple.loginwindow.plistLoginHookLogoutHook
Resolves to
LoginHook / LogoutHook script pathCommand or script executed by loginwindow policy
Enumerate
sudo defaults read /Library/Preferences/com.apple.loginwindow 2>/dev/nullfind /Users -path '*/Library/Preferences/com.apple.loginwindow.plist' 2>/dev/nulldefaults read ~/Library/Preferences/com.apple.loginwindow 2>/dev/null
Inspect
plutil -p /Library/Preferences/com.apple.loginwindow.plistplutil -p ~/Library/Preferences/com.apple.loginwindow.plist
Login / logout
Low FP · Baseline: Optional · Use: Login hook
Deprecated since 10.11. Hooks can be installed either user-scoped (~/Library/Preferences/com.apple.loginwindow.plist, current user only) or system-wide (/Library/Preferences/com.apple.loginwindow.plist, requires root, fires for every user at login). The system-wide form is the more dangerous DFIR variant and is often missed when only the user plist is checked. Execution behavior and practical support are historical / target-OS dependent, so verify on the endpoint rather than assuming a hard upper bound from the badge alone.
Startup Items [S23]
SystemLegacy; mainly pre-10.4 compatibility
Pre-launchd / compatibility
Root
DiskHigh
Artifact / Path
/Library/StartupItemsStartupParameters.plist
Resolves to
StartupItem script or executableStartupParameters.plist-defined item actually launched during boot
Enumerate
find /Library/StartupItems -maxdepth 2 -type f
Inspect
plutil -p /Library/StartupItems/*/StartupParameters.plist
Boot
Low FP · Baseline: Optional · Use: Boot autostart
Legacy mechanism mainly relevant for OS X 10.3 and earlier compatibility. From 10.4 onward, low-level service startup largely moved to launchd, but the artifact still matters in older images, migrations, and niche DFIR cases.
RC Scripts [S24]
System<=10.3
Pre-launchd
Root
DiskHigh
Artifact / Path
rc.localrc.common/private/etc/rc*
Resolves to
Script body or chained command pathActual command invoked from rc.local / rc.common
Enumerate
find /private/etc -maxdepth 2 -name 'rc*'
Inspect
grep -n '' /private/etc/rc*
Boot / init path
Low FP · Baseline: Optional · Use: Boot script
Mostly historical on current macOS: like Login Hooks and Startup Items, these mechanisms are deprecated boot paths whose presence on a modern host is itself anomalous and therefore high-signal. Keep for completeness and older-host triage; on modern fleets, treat any non-empty rc.local / rc.common as immediate priority rather than as a first-pass enumeration target.
05 Configuration Profiles & MDM
Configuration profiles (.mobileconfig) and MDM-pushed payloads are a deployment surface, not a launchd-style autostart, but they materially shape what runs and what is allowed to run on a Mac. Treat them as a first-class persistence and policy surface: a profile can install login items, allowlist a system extension or kext, grant TCC / PPPC exceptions, install root certificates, push managed preferences for any app, or enroll the device in MDM with remote-management privileges. On a compromised or social-engineered host, a single rogue profile can re-establish other persistence and silently relax defenses.
MechanismRequired privilegeSource of TruthSignalCollection / TriageTriggerWhat to review
Configuration Profiles (.mobileconfig) [S25]
MixedAll
User or device scope; install-time consent
Mixed
BothMedium
Artifact / Path
/var/db/ConfigurationProfiles/Store//var/db/ConfigurationProfiles/Settings//Library/Managed Preferences/ (resulting managed prefs)/Library/Managed Preferences/<user>/ (per-user managed prefs)Stray .mobileconfig files in Downloads / Desktop / temp
Resolves to
Installed configuration profile, its identifier, and the issuing organizationEffective managed-preference values that override per-app or per-system defaults
Enumerate
sudo profiles listsudo profiles -Psudo profiles show -type configurationsudo profiles show -type enrollmentfind /Library/Managed\ Preferences -type f 2>/dev/nullfind ~/Downloads ~/Desktop /tmp /var/tmp -name '*.mobileconfig' 2>/dev/null
Inspect
sudo profiles -P -o /tmp/profiles_export.plistsudo ls -la /var/db/ConfigurationProfiles/Store/ /var/db/ConfigurationProfiles/Settings/security cms -D -i <profile.mobileconfig>plutil -p /Library/Managed\ Preferences/<domain>.plist
Profile install / MDM push / managed pref load
Medium FP · Baseline: Critical · Use: Policy / deployment
Two install paths exist: a user double-clicks a .mobileconfig and approves it in System Settings, or an MDM server pushes it silently to an enrolled device. In both cases the on-disk truth lives under /var/db/ConfigurationProfiles/ while the enforced effects surface in /Library/Managed Preferences/. Use sudo profiles list as the first pivot: any profile whose source organization, identifier, or payload set does not match the documented enterprise baseline is high-priority. Profiles installed outside an MDM enrollment (i.e. user-approved local profiles on a managed device) are particularly suspicious. Decode raw .mobileconfig files with security cms -D -i to read their CMS-signed XML payloads. Treated as mixed scope because profiles can target the device, the current user, or both.
High-impact MDM payloads [S25a]
MixedAll; payload set evolves per release
PPPC, SystemExtensions, kext allowlists, certs, login items
Mixed
BothHigh
Artifact / Path
PayloadType com.apple.TCC.configuration-profile-policy (PPPC / privacy)PayloadType com.apple.system-extension-policyPayloadType com.apple.syspolicy.kernel-extension-policyPayloadType com.apple.servicemanagement (managed login items)PayloadType com.apple.security.pkcs1 / .pem / .root (installed certs)PayloadType com.apple.ManagedClient.preferences (custom managed prefs for any app)
Resolves to
TCC / PPPC exception that lets a chosen binary bypass user prompts for camera, mic, full disk access, AppleEvents, accessibility, etc.Allowlisted Team ID / Bundle ID for system extensions or kextsTrusted root certificate added to the system / user keychainForced login item or background service registered via SMAppService
Enumerate
sudo profiles show -type configuration | grep -E 'PayloadType|PayloadIdentifier|PayloadOrganization'sudo sqlite3 /Library/Application\ Support/com.apple.TCC/TCC.db 'SELECT service, client, auth_value, policy_id FROM access WHERE policy_id IS NOT NULL;'security find-certificate -a -p /Library/Keychains/System.keychainsystemextensionsctl list
Inspect
security cms -D -i <profile.mobileconfig> | plutil -p -security cms -V -i <profile.mobileconfig>codesign -dv --verbose=4 <binary_granted_TCC_via_profile>
Profile install / payload activation
Medium FP · Baseline: Critical · Use: Policy / privilege grant
These payload types are the ones that meaningfully change a host's security posture and that an attacker would actually want to abuse. PPPC payloads can grant a binary persistent TCC / privacy exceptions that the user would otherwise have to click through (the attacker's payload then runs with full disk access, accessibility control, or AppleEvents send rights without prompts). System-extension and kext policy payloads can pre-allowlist a third-party Team ID, removing the approval friction that normally protects extension installs. com.apple.servicemanagement can register login items / SMAppService entries from policy. Certificate payloads can install root CAs that defeat TLS pinning expectations. com.apple.ManagedClient.preferences is a wildcard: it can push managed-preference key/value pairs into any app's preference domain. Operational caveat: querying the system TCC.db requires the terminal (or your DFIR tool) to hold Full Disk Access, even with sudo, because the file is itself protected by TCC; entries with non-null policy_id are the ones installed by configuration profile rather than by user click. For each profile, decode and review the full payload list, not just the profile name; map every PPPC entry to the actual binary it empowers; correlate any new system-extension or kext allowlist with a real activation event in systemextensionsctl list or kmutil showloaded.
Quick IR Checklist
01Baseline all launchd jobs and query live state with launchctl print and print-disabled.
02Dump BTM / Login Items and map every helper to a real, signed bundle and expected install source.
03Check pre-login persistence first: LaunchDaemons, System Extensions, KEXTs, and (on macOS ≤12 only) emond.
04Diff shell startup files and resolve ZDOTDIR before concluding nothing changed.
05Enumerate scheduled execution through crontab, atq, and /etc/periodic.
06On noisy surfaces, triage by signer, Team ID, policy context, path quality, and install provenance rather than by path alone.
07For every artifact, capture signer, Team ID, path, first-seen or write time, parent process, and user context.
08Enumerate installed configuration profiles via sudo profiles list / sudo profiles show -type configuration; flag any profile not pushed by your MDM and review high-impact payloads (PPPC, system-extension and kext allowlists, certificate installs, managed login items).
Weak Signals / Priority Indicators
Recent plist in ~/Library/LaunchAgents or /Library/LaunchDaemons that points into /tmp, /private/tmp, /Users/Shared, a hidden directory, or an Apple-looking filename in a user-writable path.
RunAtLoad + KeepAlive on an unsigned or newly written target, especially when paired with WatchPaths or QueueDirectories on ~/Downloads, Desktop, temp, or shared folders.
BTM entry or embedded helper whose path is stale, user-writable, or whose signer / Team ID does not match the enclosing app.
New SSH key on a sensitive account with no corresponding admin workflow, ticket, or rotation event.
ZDOTDIR set to an unusual path or shell rc files that silently source another script from a hidden or transient location.
Unexpected third-party entry in systemextensionsctl list or a kext that appears on a host where one is not operationally expected.
New Chromium extension ID, policy-forced install, or managed profile entry; Safari appex whose signer or owner app does not fit the baseline.
.pth file containing executable import lines, or a new sitecustomize.py / usercustomize.py under an unexpected site-packages directory.
Trusted binary that suddenly loads a dylib from a user-writable path, hidden directory, or a location inconsistent with the app’s normal install layout.
Corroborating Evidence
01Signature / Team ID: confirm the persisted component, its owner app, and any helper all chain to the same signer where that is expected.
02Quarantine / provenance: inspect xattr -l for com.apple.quarantine and review how the artifact first arrived on the host.
03Timestamps: compare birthtime, mtime, and first-seen telemetry against the suspected execution or install window.
04Unified Logs: pivot to system and install events around the write or enablement time, especially for launchd, extensions, login items, and package installs.
05Process ancestry: identify what created or modified the artifact, then compare that parent process to expected install or admin workflow.
06Policy context: separate enterprise-managed state from suspicious state by checking MDM, managed preferences, forced browser installs, and approved extension workflows.
07Receipts / app ownership: correlate pkg receipts, app bundle metadata, and on-disk owner app presence before calling an embedded helper or extension malicious.
Logs / Telemetry to Check
01Unified Logs for launch activity, especially suspicious launchd labels, repeated failures, and unexpected restarts.
02backgroundtaskmanagementagent and related BTM records when Login Items or helpers look inconsistent with the installed app set.
03sysextd, systemextensionsctl output, and approval-related records when a system extension or driver stack is in scope.
04install.log and package provenance when preinstall / postinstall or helper deployment appears to have introduced persistence.
05sshd logs plus auth context for unexpected authorized_keys changes, custom AuthorizedKeysFile usage, or unusual remote access timing.
06Signer, Team ID, notarization, quarantine xattrs, mtime / birthtime, and first-seen telemetry to validate whether the artifact matches its claimed parent app or install flow.