BOOT CHAIN LAB
Lab 11 · detection engineering · home turf

Persistence, on your own Mac.

Lab 1 was persistence in the boot chain. This is the version that actually runs on the machine in front of you. An implant that survives a reboot has to register somewhere the OS will read on the way up or on login — and macOS has a fixed, well-known set of those places. That's the defender's advantage: the hiding spots are enumerable. You list them, check each item's signature and path against what belongs, and diff against a baseline. New, unsigned, and in the wrong place is the whole game.

01 — the hiding spots

Where it has to live

To survive, persistence registers with one of a handful of OS mechanisms. Each is a place you can enumerate:

LaunchAgents / LaunchDaemonsThe big one. plists in ~/Library/LaunchAgents (per-user), /Library/LaunchAgents + /Library/LaunchDaemons (system), run by launchd with RunAtLoad / KeepAlive. Most macOS malware lands here.
Login items / BTMModern background items register via SMAppService and are tracked in the Background Task Management database (macOS 13+). sfltool dumpbtm lists them; a new ad-hoc-signed helper here is a classic dropper tail.
login / logout hooksLegacy com.apple.loginwindow LoginHook running a shell script. Deprecated, still works, and rarely legitimate today — a strong signal when present.
cron / periodic / atClassic Unix scheduling still runs. A crontab entry or a script dropped into /etc/periodic is low-tech persistence that EDR sometimes overlooks.
config profiles / MDMA .mobileconfig can install payloads and settings with authority. An unexpected profile (profiles list) is persistence and policy control at once.
dylib insertionDYLD_INSERT_LIBRARIES in a plist's environment, or a hijackable @rpath, loads attacker code into a legit process on launch — ties straight to Lab 6 & 9.
02 — run the sweep

List, then judge

A snapshot of the persistence locations on a sample Mac. Run the sweep — each item is judged by who signed it (Apple / valid Developer ID / unsigned-or-ad-hoc), where its program lives (a system path vs. /tmp or a hidden dir), and whether it masquerades as something Apple. Apple system agents and known vendors pass; the odd ones out light up.

03 — how you'd really watch it

Enumerate, baseline, diff

The sweep above is a point-in-time list. The real detection is continuous: enumerate the locations, establish a baseline of what's normal for this host, and alert on anything that appears — the same baseline-then-diff from Labs 1 and 3, pointed at launchd.

# osquery — the persistence locations as tables, queryable and diffable
SELECT name,path,program,run_at_load FROM launchd
  WHERE run_at_load=1;                      # agents/daemons
SELECT * FROM startup_items;   SELECT * FROM crontab;
SELECT * FROM launchd_overrides;  # disabled→enabled flips

# eslogger — watch the locations change in real time (Endpoint Security)
eslogger btm_launch_item_add create       # new login item / plist write

# signing & profiles
codesign -dv --verbose=4 <program>   ·   spctl --assess   ·   profiles list

# reference tools (Objective-See)
KnockKnock  # enumerate every persistence point, check signatures
BlockBlock  # alert the moment something persists

A new item becomes a Vertical case (Lab 7): the plist, its signature verdict, the writing process and its lineage, captured as evidence.

04 — the hard part

Legit Macs are noisy

05 — provenance & citations

Where this comes from

tools & platform Apple — launchd, Service Management (SMAppService / BTM), Endpoint Security framework.
osquery — launchd, startup_items, crontab, launchd_overrides tables.
Objective-See (P. Wardle) — KnockKnock, BlockBlock, The Art of Mac Malware.
this lab: github.com/Mik3XC/BlueTeamLab
mapping MITRE ATT&CK — Create or Modify System Process: Launch Agent T1543.001 / Launch Daemon T1543.004; Boot/Logon Autostart: plist mod T1547.011; Boot/Logon Init Scripts T1037; Event-Triggered Execution T1546.
the thread Lab 1's persistence, now on the host you're reading this on — and the agent that drops the dylib (Lab 6/9), beacons (Lab 4), becomes the case (Lab 7).

Next: Lab 12 — DNS / DGA: expanding Lab 4's tunnel detector into domain-generation-algorithm scoring and fast-flux.