Skip to content
Categoria: Forensics8 min read

macOS Incident Forensics: UnifiedLogs, FSEvents and AULR in Practice

Por Lucas Andrade ·

How Basilisk collects evidence on macOS Sonoma and Sequoia using UnifiedLogs, FSEvents and AULR without trampling the incident scene.

macOS Incident Forensics: UnifiedLogs, FSEvents and AULR in Practice

A MacBook Pro M3 lands on the bench, suspected of running a payload signed with a revoked Developer ID. The client wants answers in 48 hours, the disk is FileVault encrypted, and the user is on vacation in another timezone. Before powering any tool, the Basilisk team locks down legal scope, receives the FileVault password through a vetted channel, and records a SHA-256 hash of the initial image. On modern macOS you do not get far without knowing that the logging stack mutated drastically since Sierra: classic .log files gave way to the binary tracev3 format, and ignoring this drops eighty percent of the available telemetry. This guide walks the three pillars a macOS responder cannot skip: UnifiedLogs, FSEvents and Apple Unified Logging with activity tracing.

Why macOS logging changed and why it matters

Since macOS Sierra, Apple replaced Apple System Log and scattered text logs with a unified, structured, binary pipeline that writes compressed tracev3 chunks. The upside for a defender is enormous: subsystem and category tags, activity IDs that stitch related events, and a retention that can reach back days or weeks. The downside is that grep over text is dead; you now need a parser that understands the format and the uuidtext string tables that rehydrate the human-readable messages. A responder who copies /var/log and calls it a day has captured almost nothing. The real evidence lives in a binary store that must be collected and parsed deliberately, which is exactly the friction this article removes.

UnifiedLogs: collection and parsing

UnifiedLogs live under /var/db/diagnostics and /var/db/uuidtext, weighing anywhere from 500 MB to 4 GB depending on usage. For live capture we run log collect --output incident.logarchive, which freezes the current state into a portable bundle you can analyze off-box. For dead-box work we copy the raw directories and feed them to Mandiant's macos-UnifiedLogs, a Rust parser that no longer requires a matching Apple host. That used to be a major friction point: you had to keep a Mac with the exact target OS version online just to invoke /usr/bin/log. Stream the parsed output to JSONL so it flows straight into your analysis pipeline. For the broader defensive context, pair this read with macOS Hardening: Lockdown Mode, MDM and Attack Surface Reduction.

FSEvents: what changed on this volume and when

FSEvents is the second pillar and answers the 'what changed on this volume and when' question. Logs sit in /.fseventsd/ as gzipped numbered files; each record carries a monotonic event ID plus flags for create, rename or delete. Critical caveat: FSEvents records neither content nor the user who triggered the change, only path and operation. Pairing FSEvents with UnifiedLogs produces a trustworthy timeline, because the log tells you which process ran while FSEvents tells you which files it touched. Tools like David Cowen's FSEventsParser chew through it in seconds and emit Timesketch-ready CSV, echoing the workflow we covered in Timeline Forensics on Windows: Plaso, Log2Timeline and KAPE in Practice. Treat the monotonic ID as your ordering key when wall-clock timestamps are missing.

AULR and the power of predicates

AULR, or more precisely Apple Unified Logging with Activity Tracing, layers parent process, thread ID and signpost context on top. Predicates are your sharpest knife: log show --predicate 'subsystem == "com.apple.securityd"' --last 24h surfaces suspicious XPC attempts, while subsystem com.apple.TCC exposes microphone and camera prompts that were denied or granted. In a real June case this year, an Atomic AMOS stealer variant left fingerprints in com.apple.kextd trying to load a KEXT on a SIP-enabled Mac, failing loudly. Without that predicate the event drowns in millions of lines of noise. Build a small library of predicates for persistence, TCC, code signing and network subsystems so you never grep blind through a firehose. Useful starting predicates include eventMessage CONTAINS "amfid" for code-signing rejections, process == "tccd" for privacy grants, and subsystem == "com.apple.network" for connection attempts; save each with a note on what it proves so a teammate can reuse it under pressure without re-deriving the syntax.

Ethical capture and chain of custody

On the ethical capture side, Basilisk follows a fixed runbook: written client authorization, a Thunderbolt write-blocker for disk imaging when feasible, SHA-256 and SHA-3-512 hashes, and chain-of-custody logs signed with YubiKey hardware keys. When the Mac is live and cannot be shut down we lean on CrowdStrike's aftriage or the macos_artifact_collection Velociraptor recipe, always redirecting output to a dedicated APFS external SSD. This rigor mirrors what we explored in DFIR on Linux: Live Triage with UAC and Velociraptor and connects with the personal discipline in OPSEC for Security Researchers: Building a Personal Threat Model. Record every command you run with timestamps, because the report is only as strong as its reproducibility.

Correlating the wider artifact set

The three pillars are the skeleton; the flesh is the rest of the artifact set. Inspect Spotlight metadata via mdls on suspicious files, export KnowledgeC.db from CoreDuet to map focused windows, and walk /private/var/db/CoreDuet/Knowledge to correlate Terminal usage with off-hours activity. For persistence, enumerate LaunchAgents and LaunchDaemons in the user and system domains, check BTM (Background Task Management) records, and diff installed configuration profiles. When loader behavior surfaces it tends to rhyme with techniques covered in Malware Analysis in an Isolated Lab: Safe Setup with FlareVM and REMnux, so pivot a sample into a sandbox rather than detonating it on the evidence host.

Building the timeline

Analysis starts in parallel: while macos-UnifiedLogs streams JSONL in the background, we load FSEvents CSV and the log output into a Jupyter notebook with pandas and join UnifiedLogs PIDs against FSEvents paths on a shared time window. The goal is a single super-timeline where a process start, a file creation, a TCC prompt and a network connection line up within seconds of each other and tell a coherent story. Push the merged frame into Timesketch for collaborative review and tagging, and use its saved views to isolate the suspected intrusion window so reviewers are not scrolling through unrelated boot noise. A timeline that survives cross-examination is one where every row cites its source artifact, so keep the provenance column populated from the first import rather than reconstructing it later under deadline pressure.

Anti-forensics and common pitfalls

Attackers know these artifacts too. A capable intruder may clear UnifiedLogs with a signed helper, tamper with system time to scramble ordering, or operate entirely in memory to avoid FSEvents. Watch for gaps: a suspiciously clean tracev3 window, an FSEvents sequence with a jump in event IDs, or a log that restarts abruptly all suggest tampering rather than innocence. The most common self-inflicted pitfall is analyst-side: booting the evidence, letting Spotlight re-index, or mounting read-write and thereby writing new FSEvents that pollute the very timeline you came to read. Always mount read-only and work from an image, never the original. A second frequent error is trusting wall-clock time on a machine whose clock an attacker moved; anchor your timeline to monotonic FSEvents IDs and to externally verifiable events such as DHCP leases or server-side logs, and flag any interval where the local clock disagrees with those anchors as suspect rather than authoritative.

A responder checklist

Condense the engagement into a repeatable checklist: confirm written authorization and scope; obtain the FileVault key through a vetted channel; image with a write-blocker and hash with SHA-256; run log collect and copy /.fseventsd/; parse with macos-UnifiedLogs and FSEventsParser; pull KnowledgeC, TCC, LaunchAgents and BTM; build the super-timeline in pandas and Timesketch; check for anti-forensic gaps; and sign the chain of custody at every step. Keep the checklist in the case folder and tick it as you go, because a missed FileVault key or an unhashed image can void the entire engagement no matter how good the analysis that follows.

FAQ: can I analyze UnifiedLogs without a matching Mac?

Yes. That used to require an Apple host running the same OS version to call /usr/bin/log, which was a real constraint for dead-box work. Mandiant's macos-UnifiedLogs is a standalone Rust parser that reads the raw tracev3 and uuidtext directories on any platform, so you can process an image on a Linux workstation. Keep a note of the target OS version anyway, because message formats occasionally shift between releases and knowing the version helps you interpret ambiguous entries correctly.

FAQ: how long does UnifiedLogs retain data?

It depends on volume and disk pressure rather than a fixed window; heavy logging can compress retention to a couple of days while a quiet machine may keep weeks. That variance is exactly why you collect early: the moment an incident is suspected, run log collect and snapshot /.fseventsd/ before normal activity rotates the oldest chunks out. Retention is not a guarantee, it is a race, and the responder who captures on day zero has evidence the responder who waits a week simply does not.

Practical takeaway: build a baseline logarchive of your own Mac today with log collect and stash it next to an APFS snapshot of /.fseventsd. Next time something feels off you will have a real temporal diff instead of guesses. That baseline costs five minutes and spares you days of reactive investigation when the incident actually knocks, turning a panicked 48-hour scramble into a calm comparison against known-good ground truth.

Related posts

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly