Chapter 46

macOS Forensics

macOS is the primary platform for developers, executives, and design teams — and a common entry point for targeted attacks. macOS has a unique artifact set: Unified Logging (replacing traditional syslog), FSEvents (comprehensive filesystem change journaling), plist files for persistence, and Apple-specific databases for Spotlight and Safari.

Scenario

An executive's MacBook was targeted by a spear-phishing campaign that delivered a malicious DMG file. The attacker installed a macOS implant (LaunchAgent for persistence) and exfiltrated documents from the Desktop and Documents folders. The Unified Log captures the DMG mount, the app execution, and the LaunchAgent installation. FSEvents records every file accessed by the implant. This chapter covers collecting and analyzing these macOS-specific artifacts.

macOS Forensic Artifact Locations

ArtifactLocationWhat it records
Unified Log/var/db/diagnostics/ + /var/db/uuidtext/All system events — replaceed old syslog, process activity, network events
FSEvents/.fseventsd/Filesystem change journal — every create, modify, rename, delete (content, not timestamps)
LaunchAgents (user)~/Library/LaunchAgents/*.plistPer-user persistence — starts on login
LaunchDaemons/Library/LaunchDaemons/*.plistSystem-wide persistence — starts at boot
Shell history~/.zsh_history (macOS Catalina+), ~/.bash_historyCommand-line history
Quarantine database~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2All downloaded files with URL and timestamp
Spotlight database/.Spotlight-V100/Metadata index of all files — also tracks deleted files for a period
Safari/Chrome history~/Library/Safari/History.db, ~/Library/Application Support/Google/Chrome/Default/HistoryWeb browsing history
Bashmacos-triage.sh
#!/bin/bash
# macOS forensic triage collection
# Run with sudo on the compromised Mac or mounted image

TARGET="/"     # live system or set to mounted image path
OUT="/Volumes/EvidenceDrive/CASE-2026-009/$(hostname)"
mkdir -p $OUT/{logs,persistence,users,downloads}

# Unified Log (binary format — convert with 'log' command)
echo "Collecting Unified Log..."
# Export from running system:
log collect --last 7d --output $OUT/logs/unified-log.logarchive

# Or from offline mounted image:
# log show --archive $TARGET/var/db/diagnostics --info --debug \
#     --predicate 'subsystem == "com.apple.launchd"' \
#     > $OUT/logs/launchd.txt

# FSEvents (requires fseventer or dedicated parser)
echo "Collecting FSEvents..."
cp -R "$TARGET/.fseventsd/" "$OUT/logs/fseventsd/"

# LaunchAgents / LaunchDaemons (persistence)
echo "Collecting persistence..."
cp -R "$TARGET/Library/LaunchDaemons/" "$OUT/persistence/"
cp -R "$TARGET/Library/LaunchAgents/" "$OUT/persistence/system-launch-agents/"
for user_dir in $TARGET/Users/*/; do
    user=$(basename $user_dir)
    mkdir -p "$OUT/persistence/users/$user"
    cp -R "$user_dir/Library/LaunchAgents/" "$OUT/persistence/users/$user/" 2>/dev/null
done

# Quarantine database (downloaded files with URLs)
echo "Collecting quarantine events..."
for user_dir in $TARGET/Users/*/; do
    user=$(basename $user_dir)
    qdb="$user_dir/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2"
    if [ -f "$qdb" ]; then
        cp "$qdb" "$OUT/downloads/QuarantineEvents-$user.db"
    fi
done

# User home artifacts
for user_dir in $TARGET/Users/*/; do
    user=$(basename $user_dir)
    mkdir -p "$OUT/users/$user"
    cp "$user_dir/.zsh_history" "$OUT/users/$user/" 2>/dev/null
    cp "$user_dir/.bash_history" "$OUT/users/$user/" 2>/dev/null
    cp -R "$user_dir/.ssh/" "$OUT/users/$user/" 2>/dev/null
done

Unified Log Analysis

Bashunified-log-analysis.sh
LOGARCHIVE="/cases/CASE-2026-009/exec-mac/logs/unified-log.logarchive"

# Show all process launches in a time window
log show --archive $LOGARCHIVE \
    --start "2026-09-17 00:00:00" \
    --end "2026-09-17 06:00:00" \
    --predicate 'process == "launchd"' \
    --info | grep -i "spawn\|exec"

# Find network connections (endpointsecurity subsystem)
log show --archive $LOGARCHIVE \
    --predicate 'subsystem == "com.apple.endpointsecurity"' \
    --info | grep -i "connect\|bind"

# Find LaunchAgent installations (persistence events)
log show --archive $LOGARCHIVE \
    --predicate 'process == "launchd" AND message CONTAINS "LaunchAgent"' \
    --info

# Find app installations from DMG
log show --archive $LOGARCHIVE \
    --predicate 'process == "diskimagemounter"' \
    --info | grep -i "mount\|attach"

Quarantine Database Analysis

Bashquarantine-db.sh
QDBPATH="/cases/CASE-2026-009/exec-mac/downloads/QuarantineEvents-jdoe.db"

# Query the quarantine database (SQLite)
sqlite3 $QDBPATH << 'EOF'
.headers on
.mode column
.width 30 15 50 30

-- All downloaded files with origin URLs
SELECT
    datetime(LSQuarantineTimeStamp + 978307200, 'unixepoch') AS DownloadTime,
    LSQuarantineAgentBundleIdentifier AS App,
    LSQuarantineDataURLString AS URL,
    LSQuarantineOriginURLString AS ReferrerURL
FROM LSQuarantineEvent
ORDER BY LSQuarantineTimeStamp DESC
LIMIT 100;
EOF
# Note: LSQuarantineTimeStamp is Apple Absolute Time (seconds since Jan 1 2001)
# 978307200 converts to Unix epoch

Q & A

Q: The Mac is an M1/M2 (Apple Silicon). Does this change the forensic process?

The artifact types and locations are the same between Intel and Apple Silicon Macs. The key differences for forensics are: (1) Acquisition: Apple Silicon Macs have a Secure Enclave and different boot process. DFU mode (Device Firmware Upgrade) is required for some acquisition techniques, and the process differs from Intel Macs' Target Disk Mode. checkra1n (jailbreak-based acquisition) doesn't support Apple Silicon. Tools like Cellebrite UFED and Sumuri Paladin have been updated for M-series; verify your toolchain supports Apple Silicon before acquisition. (2) Memory: Apple Silicon uses unified memory architecture — RAM and GPU share a memory pool. Standard macOS memory acquisition tools work similarly, but the memory layout differs. (3) Binaries: Apple Silicon runs ARM64 binaries or x86 under Rosetta 2 translation. If an attacker dropped an ARM64 binary, tools running on x86 analysis VMs can't execute it directly — use `file` to identify the architecture, and static analysis tools (Ghidra, Binary Ninja) for examination. (4) File System: APFS is the default on both architectures — artifact locations remain the same.