Skip to content

[New Rules] macOS Unified Logs TCC Detection Rules#5870

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

[New Rules] macOS Unified Logs TCC Detection Rules#5870
DefSecSentinel wants to merge 17 commits intomainfrom
colson/tcc-event-unified-log-detections

Conversation

@DefSecSentinel
Copy link
Contributor

Summary

  • Adds 5 new alerting rules leveraging macOS Unified Logs TCC (Transparency, Consent, and Control) telemetry to detect suspicious permission access and changes
  • All rules target the com.apple.TCC subsystem with category access and work without private data enablement
  • Companion to #5867 (Apple Event detections)

New Alerting Rules

Rule Severity MITRE Pattern
Full Disk Access Denied via TCC low T1005 kTCCServiceSystemPolicyAllFiles + auth_value=0
Screen Capture Access Denied via TCC low T1113 kTCCServiceScreenCapture + auth_value=0
Camera or Microphone Access Denied via TCC low T1125, T1123 kTCCServiceCamera/kTCCServiceMicrophone + auth_value=0
TCC Permission Probing Without Prompt Entitlement medium T1518.001 does not allow prompting
TCC Privacy Permission Change Detected medium T1562.001 publishAccessChangedEvent

Design Notes

  • All rules use EQL with message like for pattern matching on raw message field
  • Adds message: keyword to non-ecs-schema.json for logs-unified_logs.log-* to support EQL validation (same fix as Apple Events PR)
  • Rules 7-9 (denied access) are low severity BBR rules intended to be correlated with other indicators
  • Rule 10 (permission probing) detects apps attempting to access services they have no entitlement to prompt for — a strong indicator of malicious reconnaissance
  • Rule 11 (permission change) detects TCC database modifications that could indicate privilege escalation or defense evasion
  • Maturity set to development given the beta integration status
  • Based on Phase 3 research: 2,957 TCC events analyzed, 34 distinct kTCCService types observed

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

Test plan

  • Validate TOML structure passes detection_rules validate-rule
  • Deploy rules against test environment with Unified Logs integration configured for TCC predicate (subsystem=="com.apple.TCC" AND category=="access")
  • Trigger TCC denials by running unsigned apps that request FDA, screen capture, camera, and microphone access
  • Verify permission probing rule fires for apps without prompt entitlements
  • Trigger TCC permission changes via System Settings and verify rule 11 fires
  • Confirm acceptable false positive rate against baseline macOS activity

🤖 Generated with Claude Code

Adds 5 new alerting rules leveraging macOS Unified Logs TCC (Transparency,
Consent, and Control) telemetry from the com.apple.TCC subsystem to detect
suspicious permission access and changes.

New rules:
- Full Disk Access Denied via TCC (T1005)
- Screen Capture Access Denied via TCC (T1113)
- Camera or Microphone Access Denied via TCC (T1125, T1123)
- TCC Permission Probing Without Prompt Entitlement (T1518.001)
- TCC Privacy Permission Change Detected (T1562.001)

Also adds message field as keyword in non-ECS schema for the
logs-unified_logs.log-* index pattern to support EQL validation.

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
  • ❌ Full Disk Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@DefSecSentinel DefSecSentinel self-assigned this Mar 23, 2026
@DefSecSentinel DefSecSentinel added dev rule meant to be non-prod / non-shipping OS: macOS 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.

@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Full Disk Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@tradebot-elastic
Copy link

tradebot-elastic commented Mar 23, 2026

⛔️ Test failed

Results
  • ❌ Full Disk Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (eql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

Copy link
Contributor

@terrancedejesus terrancedejesus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few thoughts and suggestions. Logic seems sound!

[metadata]
creation_date = "2026/03/23"
integration = ["unified_logs"]
maturity = "development"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want to check other PRs but this says development, not production. Won't release if not production.

rule_id = "a96b3d0f-beb1-413d-b498-114ad6d26965"
severity = "medium"
tags = [
"Domain: macOS",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be endpoint as the domain.

OS would be macOS.

Switch from EQL to KQL (query type) since message is match_only_text
and EQL doesn't support text fields. Use unified_log.subsystem and
unified_log.category keyword fields for efficient filtering. Update
index pattern 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
  • ❌ Full Disk Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

The ingest pipeline normalizes event.dataset to "unifiedlogs.log"
regardless of the data stream name. Update all rules and index
patterns accordingly.

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
  • ❌ Full Disk Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (kuery)
    • 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
  • ❌ Full Disk Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (kuery)
    • 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
  • ❌ Full Disk Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (kuery)
    • 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
  • ❌ Full Disk Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (kuery)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

KQL cannot query the message field (match_only_text type with no
keyword sub-field). ES|QL's LIKE operator works correctly on text
fields. Converted all 5 TCC rules to ES|QL with METADATA and KEEP.

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
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

user.name, process.name, process.executable don't exist in
the unifiedlogs index. Removed from KEEP to prevent query errors.

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
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

event.dataset is unifiedlogs.log which maps to package=unifiedlogs,
integration=log. Schema needs a 'log' dataset key.

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
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@DefSecSentinel
Copy link
Contributor Author

Testing & Validation Update

All TCC 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 all rules from KQL to ES|QLmessage is a text field that doesn't support KQL term matching
  • Fixed TCC denial pattern from auth_value=0 to Handling access request + NOT Allowedauth_value=0 appears in separate REPLY_MSG messages without the service name
  • Fixed event.dataset, index pattern, and integration tag to match actual data stream naming
  • Added integration manifest and schema via CLI
  • Removed non-existent fields from ES|QL KEEP clause

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

Full Disk Access Denied:

ls ~/Library/Mail/ 2>/dev/null
cat ~/Library/Safari/History.db 2>/dev/null

Screen Capture Denied:

screencapture -x /tmp/test_capture.png
# Click Deny if prompted

Permission Probing (no prompt entitlement):

python3 -c "
import ctypes, ctypes.util
appservices = ctypes.cdll.LoadLibrary(ctypes.util.find_library('ApplicationServices'))
result = appservices.AXIsProcessTrusted()
print(f'Accessibility trusted: {result}')
"

TCC Permission Change:

tccutil reset Camera com.apple.Terminal

Integration Prerequisites

  • Unified Logs integration installed on macOS agent policy
  • Debug and Info toggles enabled in integration settings
  • Predicate configured to capture TCC subsystem:
    subsystem=="com.apple.TCC" AND category=="access"
    

Validation Results

Rule Emulation Query Match Status
Full Disk Access Denied Read FDA-protected paths kTCCServiceSystemPolicyAllFiles + Handling access request ✅ Confirmed
Screen Capture Denied screencapture (deny prompt) kTCCServiceScreenCapture + Handling access request ✅ Confirmed
Camera/Mic Denied Same pattern as above kTCCServiceCamera/kTCCServiceMicrophone ⚠️ Pattern validated (same format as FDA/screen capture)
Permission Probing AXIsProcessTrusted() from unsigned binary does not allow prompting ✅ Confirmed
TCC Permission Change tccutil reset Camera publishAccessChangedEvent ✅ Confirmed

@DefSecSentinel DefSecSentinel marked this pull request as ready for review March 24, 2026 14:34
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
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

- Flatten duplicate Collection tactic in camera/mic rule
- Rename FDA and screen capture filenames: discovery_ -> collection_
- Fix FDA tactic tag: Discovery -> Collection
- Add "Resources: Investigation Guide" tag to all TCC rules

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
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta

@tradebot-elastic
Copy link

tradebot-elastic commented Mar 24, 2026

⛔️ Test failed

Results
  • ❌ Full Disk Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Camera or Microphone Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Privacy Permission Change Detected (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ Screen Capture Access Denied via TCC (esql)
    • coverage_issue: no_rta
    • stack_validation_failed: no_rta
  • ❌ TCC Permission Probing Without Prompt Entitlement (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

backport: auto dev rule meant to be non-prod / non-shipping integration: Unified_Logs OS: macOS patch Rule: New Proposal for new rule

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants