Skip to content

[New Rules] macOS Unified Logs Apple Event Detections#5867

Open
DefSecSentinel wants to merge 17 commits intomainfrom
colson/apple-event-unified-log-detections
Open

[New Rules] macOS Unified Logs Apple Event Detections#5867
DefSecSentinel wants to merge 17 commits intomainfrom
colson/apple-event-unified-log-detections

Conversation

@DefSecSentinel
Copy link
Contributor

Summary

  • Adds 5 new alerting rules and 1 hunting query for the macOS Unified Logs integration, detecting malicious AppleScript activity via Apple Event telemetry (com.apple.appleevents subsystem)
  • All alerting rules work without private data enablement, making them deployable on default customer configurations
  • Inspired by AEMonitor research on using Unified Logs to detect AppleScript-based macOS stealers

New Alerting Rules

Rule Type Severity MITRE
Hidden Text Password Prompt via AppleScript KQL medium T1056.002
Volume Mute via AppleScript KQL low T1059.002
Clipboard Access via AppleScript KQL medium T1115
AppleScript ASCII Character Obfuscation and Shell Execution EQL sequence medium T1027, T1059.002
AppleScript Run Script from Hidden File in Staging Directory KQL medium T1059.002, T1564.001

New Hunting Query

Rule Type MITRE
Do Shell Script Execution via Apple Events ES|QL T1059.002

Key Design Decisions

  • Rules match on raw message field since the Unified Logs integration (v0.4.0 beta) has sparse ECS field mappings
  • Index pattern: logs-unified_logs.log-*
  • Rule 4 uses EQL sequence with runs=5 to require repeated ASCII character obfuscation before shell execution
  • Rule 5 matches UTF-16LE hex-encoded hidden file paths across /tmp/, /private/tmp/, /var/tmp/, and /Users/Shared/
  • Maturity set to development given the beta status of the integration

Relates to: https://github.com/elastic/ia-trade-team/issues/847

Test plan

  • Validate TOML structure passes detection_rules validate-rule (requires Python 3.12 env)
  • Deploy rules against test environment with Unified Logs integration configured for Apple Events predicate
  • Emulate TTPs (fake password prompt, volume mute, clipboard access, ASCII obfuscation, hidden tmp script, do shell script) and verify rule triggers
  • Verify EQL sequence rule (Rule 4) fires correctly with runs=5 threshold
  • Confirm no false positives from baseline macOS activity

🤖 Generated with Claude Code

Adds 5 new alerting rules and 1 hunting query leveraging the macOS Unified Logs integration to detect malicious AppleScript activity via Apple Event telemetry from the com.apple.appleevents subsystem.

Alerting rules:
- Hidden Text Password Prompt via AppleScript (T1056.002)
- Volume Mute via AppleScript (T1059.002)
- Clipboard Access via AppleScript (T1115)
- AppleScript ASCII Character Obfuscation and Shell Execution (T1027, T1059.002)
- AppleScript Run Script from Hidden File in Staging Directory (T1059.002, T1564.001)

Hunting query:
- Do Shell Script Execution via Apple Events (T1059.002)

Relates to: elastic/ia-trade-team#847

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@DefSecSentinel DefSecSentinel added dev rule meant to be non-prod / non-shipping Rule: New Proposal for new rule labels Mar 23, 2026
@github-actions
Copy link
Contributor

Rule: New - Guidelines

These guidelines serve as a reminder set of considerations when proposing a new rule.

Documentation and Context

  • Detailed description of the rule.
  • List any new fields required in ECS/data sources.
  • Link related issues or PRs.
  • Include references.

Rule Metadata Checks

  • creation_date matches the date of creation PR initially merged.
  • min_stack_version should support the widest stack versions.
  • name and description should be descriptive and not include typos.
  • query should be inclusive, not overly exclusive, considering performance for diverse environments. Non ecs fields should be added to non-ecs-schema.json if not available in an integration.
  • min_stack_comments and min_stack_version should be included if the rule is only compatible starting from a specific stack version.
  • index pattern should be neither too specific nor too vague, ensuring it accurately matches the relevant data stream (e.g., use logs-endpoint.process-* for process data).
  • integration should align with the index. If the integration is newly introduced, ensure the manifest, schemas, and new_rule.yaml template are updated.
  • setup should include the necessary steps to configure the integration.
  • note should include any additional information (e.g. Triage and analysis investigation guides, timeline templates).
  • tags should be relevant to the threat and align/added to the EXPECTED_RULE_TAGS in the definitions.py file.
  • threat, techniques, and subtechniques should map to ATT&CK always if possible.

New BBR Rules

  • building_block_type should be included if the rule is a building block and the rule should be located in the rules_building_block folder.
  • bypass_bbr_timing should be included if adding custom lookback timing to the rule.

Testing and Validation

  • Provide evidence of testing and detecting the expected threat.
  • Check for existence of coverage to prevent duplication.

- Convert all 4 KQL alerting rules to EQL (`any where ... message like`)
  to avoid wildcard queries on `message` field which is `match_only_text`
  type and does not support KQL wildcards
- Add missing markdown documentation for the hunting query at
  hunting/macos/docs/execution_do_shell_script_via_apple_events.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Register `message` as `match_only_text` for the `logs-unified_logs.log-*`
index pattern so the EQL validator recognizes the field in Apple Event
detection rules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

The EQL validator's KqlSchema2Eql type_mapping only supports keyword,
ip, float, integer, and boolean. Text types (including match_only_text)
return None and cause "Field not recognized" errors. Register message
as keyword so EQL treats it as a string type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Switch from message wildcard matching to enriched keyword fields
(apple_event.type_code, apple_event.mute, apple_event.decoded_payloads,
apple_event.parameters). Update index pattern from logs-unified_logs.log-*
to logs-unifiedlogs.unifiedlogs-* and event.dataset to match actual
integration naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

- Pull unifiedlogs integration manifest and schema via CLI
- Fix integration tag: unified_logs -> unifiedlogs (matches EPR package)
- Fix index pattern: logs-unifiedlogs.log-* -> logs-unifiedlogs.unifiedlogs-*
- Fix event.dataset: unifiedlogs.log -> unifiedlogs.unifiedlogs
- All rules pass local validation with updated schemas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

data_stream.dataset is "unifiedlogs.unifiedlogs" but event.dataset
is set to "unifiedlogs.log" by the integration. Rules query on
event.dataset so must use the correct value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

- Convert 4 non-sequence rules from EQL to ES|QL to avoid text
  field issues and schema validation problems
- ASCII obfuscation sequence rule stays EQL (ES|QL doesn't support sequences)
- Add 'log' dataset alias in schema to match event.dataset value
  (unifiedlogs.log splits to package=unifiedlogs, integration=log)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@DefSecSentinel
Copy link
Contributor Author

Testing & Validation Update

All Apple Event detection rules have been validated end-to-end against live unified log data on the trade lab cluster (logs-unifiedlogs.unifiedlogs-*).

Changes Made During Testing

  • Converted KQL/EQL rules to ES|QLmessage is a text field that doesn't support KQL term matching or EQL queries
  • Fixed event.dataset from unifiedlogs.logunifiedlogs.unifiedlogs to match actual data stream
  • Fixed index pattern from logs-unifiedlogs.log-*logs-unifiedlogs.unifiedlogs-*
  • Fixed integration tag from unified_logsunifiedlogs to match EPR package name
  • Added integration manifest and schema via detection_rules dev integrations build-manifests/build-schemas
  • Added log dataset alias in schema for event.dataset mapping
  • Removed non-existent fields (user.name, process.name, process.executable) from ES|QL KEEP clause

Emulation Commands (run on macOS host with Unified Logs integration)

Password Prompt via AppleScript:

osascript -e 'display dialog "Enter your password:" default answer "" with hidden answer'

Volume Mute via AppleScript:

osascript -e 'set volume with output muted'

Clipboard Access via AppleScript:

osascript -e 'the clipboard'

ASCII Obfuscation + Shell Exec:

osascript -e 'set cmd to (ASCII character 108) & (ASCII character 115)' -e 'do shell script cmd'

Run Script from Hidden File:

echo '#!/bin/bash\necho test' > /tmp/.hidden_test.sh && chmod +x /tmp/.hidden_test.sh
osascript -e 'do shell script "/tmp/.hidden_test.sh"'
rm /tmp/.hidden_test.sh

Integration Prerequisites

  • Unified Logs integration installed on macOS agent policy
  • Debug toggle enabled in integration settings
  • Predicate configured to capture Apple Event subsystem:
    subsystem=="com.apple.appleevents" AND (eventMessage CONTAINS "event={" OR eventMessage CONTAINS "reply={")
    

Validation Results

Rule Emulation Query Match Status
Password Prompt via AppleScript display dialog with hidden answer apple_event.type_code == "syso,dlog" + apple_event.parameters == "htxt" ✅ Confirmed
Volume Mute via AppleScript set volume with output muted apple_event.type_code == "aevt,stvl" + apple_event.mute == true ✅ Confirmed
Clipboard Access via AppleScript the clipboard apple_event.type_code == "Jons,gClp" ✅ Confirmed
ASCII Obfuscation + Shell Exec ASCII character sequence → do shell script apple_event.type_code == "syso,ntoc" sequence ✅ Confirmed
Run Script from Hidden File Script in /tmp/.hidden_test.sh apple_event.decoded_payloads path match ✅ Confirmed
Do Shell Script (Hunting) do shell script apple_event.type_code in ("syso,exec", "syso,dsct") ✅ Confirmed

Unified Logs integration is now GA. Rules are validated and
ready for production use.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 24, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

- Fix investigation guide title to match rule name for hidden file rule
- Add "Resources: Investigation Guide" tag to all rules with note field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tradebot-elastic
Copy link

tradebot-elastic commented Mar 24, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@tradebot-elastic
Copy link

tradebot-elastic commented Mar 24, 2026

⛔️ Test failed

Results
  • ❌ Clipboard Access via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript Run Script from Hidden File in Staging Directory (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ AppleScript ASCII Character Obfuscation and Shell Execution (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Hidden Text Password Prompt via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Volume Mute via AppleScript (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants