Skip to content

Add Linux Auditd Audit Rules Removed detection (T1562.001) - #4386

Open
vahidmalekk wants to merge 2 commits into
splunk:developfrom
vahidmalekk:detection/linux-auditd-rules-removed
Open

vahidmalekk wants to merge 2 commits into
splunk:developfrom
vahidmalekk:detection/linux-auditd-rules-removed

Conversation

@vahidmalekk

Copy link
Copy Markdown

Summary

Adds a new anomaly analytic, Linux Auditd Audit Rules Removed, plus a new Linux Auditd Config Change data source, detecting a runtime flush of the audit ruleset (T1562.001 / T1070) via the native CONFIG_CHANGE op=remove_rule events.

Coverage gap

ESCU has auditd daemon/service-stop detections, but an attacker who runs auditctl -D (or an AUDIT_DEL netlink) flushes the ruleset while the daemon keeps running - no DAEMON_END fires, so every existing auditd rule stays silent even though host auditing is now blind. This closes that gap. It complements the companion PR Linux Auditd Audit Configuration File Modification (on-disk config) - this one covers the runtime flush.

How it works / FP handling

The kernel emits a CONFIG_CHANGE record per rule add/removal (no custom audit rule needed). Legitimate reloads - augenrules --load, auditctl -R, service restart, boot - remove then re-add the rules in the same window, so the search fires only when rules_removed >= 5 and rules_added = 0 (a flush, not a reload). Validated live: a real reload removed 1856 / added 2056 in one minute and was correctly suppressed.

New data source

data_sources/linux_auditd_config_change.yml (separator type = CONFIG_CHANGE) - there was no existing CONFIG_CHANGE auditd data source. Happy to adjust its fields/naming to STRT conventions.

Testing

True-positive dataset captured on an authorized lab host: eight op=remove_rule events with no matching add_rule. Paired attack data: splunk/attack_data#1261; tests.attack_data points at it, so the unit test passes once #1261 merges.

MITRE ATT&CK

T1562.001 (Impair Defenses: Disable or Modify Tools), T1070 (Indicator Removal)

The rules_removed>=5 threshold (flush vs single-rule tuning) and the system-context exclusion are documented as tunable in how_to_implement / the filter macro.

Defines the CONFIG_CHANGE auditd data source (audit rule add/remove at
runtime) used by the Linux Auditd Audit Rules Removed detection.
Detects a runtime flush of audit rules (CONFIG_CHANGE op=remove_rule with
no matching add_rule in the window) - auditctl -D / AUDIT_DEL - which
blinds auditing while the daemon keeps running (no DAEMON_END). Legit
reloads re-add rules and are excluded by rules_added=0. T1562.001 / T1070.
@nasbench

nasbench commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T11:19:10.197316Z f351e10 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f351e10757

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

search: |-
`linux_auditd` type=CONFIG_CHANGE op IN ("remove_rule", "add_rule")
| bin _time span=1m
| stats count(eval(op=="remove_rule")) as rules_removed count(eval(op=="add_rule")) as rules_added values(key) as removed_keys min(_time) as firstTime max(_time) as lastTime BY _time host auid ses subj

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve removals when subj is absent

CONFIG_CHANGE records can omit subj on hosts without an active LSM security context, and the new data source does not require that field. Splunk's stats ... BY ... subj drops events with no value for that grouping field, so a qualifying auditctl -D on such a host produces no row and no detection; coalesce the optional context before stats or do not group on it.

Useful? React with 👍 / 👎.

`linux_auditd` type=CONFIG_CHANGE op IN ("remove_rule", "add_rule")
| bin _time span=1m
| stats count(eval(op=="remove_rule")) as rules_removed count(eval(op=="add_rule")) as rules_added values(key) as removed_keys min(_time) as firstTime max(_time) as lastTime BY _time host auid ses subj
| where rules_removed>=5 AND rules_added=0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require re-additions to correspond to removed rules

If an attacker flushes the rules and then adds one arbitrary harmless rule in the same minute and session, rules_added becomes 1 and this predicate discards the group even if hundreds of removed rules remain absent. Because a legitimate reload is expected to restore the removed rules, treating any single add as a reload gives the attacker targeted by this detection a trivial bypass; compare the re-added rules or counts with the removals instead.

Useful? React with 👍 / 👎.

- Linux Auditd Config Change
search: |-
`linux_auditd` type=CONFIG_CHANGE op IN ("remove_rule", "add_rule")
| bin _time span=1m

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Correlate reloads across minute boundaries

When a legitimate reload starts near a wall-clock minute boundary, this fixed bin places its removal records in the preceding bucket and its add records in the following bucket. The first bucket can therefore satisfy rules_removed>=5 AND rules_added=0 and raise the high-severity result even though the rules were immediately restored, contradicting the documented reload exclusion; correlate over a sliding reload window rather than an aligned one-minute bucket.

Useful? React with 👍 / 👎.

search: |-
`linux_auditd` type=CONFIG_CHANGE op IN ("remove_rule", "add_rule")
| bin _time span=1m
| stats count(eval(op=="remove_rule")) as rules_removed count(eval(op=="add_rule")) as rules_added values(key) as removed_keys min(_time) as firstTime max(_time) as lastTime BY _time host auid ses subj

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude failed rule-removal operations

CONFIG_CHANGE can emit op=remove_rule for rejected deletion requests with res=no/res=0, but this count ignores res. Five failed requests in one minute therefore meet the threshold and generate a risk event claiming rules were removed even though the ruleset is unchanged; count only successful removals, or report failed attempts separately.

Useful? React with 👍 / 👎.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants