diff --git a/rules/cross-platform/credential_access_grep_recursive_credential_discovery.toml b/rules/cross-platform/credential_access_grep_recursive_credential_discovery.toml new file mode 100644 index 000000000..ce867250b --- /dev/null +++ b/rules/cross-platform/credential_access_grep_recursive_credential_discovery.toml @@ -0,0 +1,136 @@ +[metadata] +creation_date = "2026/03/25" +integration = ["endpoint"] +maturity = "production" +updated_date = "2026/03/25" + +[rule] +author = ["Elastic"] +description = """ +Identifies recursive grep activity on Linux or macOS where the command line suggests hunting for secrets, credentials, +keys, tokens, or sensitive paths (for example .env, .git, .aws). Events are aggregated per host, user, parent process, +and one-minute window, the rule surfaces activity only when at least three distinct grep command lines match in the same +bucket, to reduce noise from one-off searches. +""" +from = "now-9m" +interval = "5m" +language = "esql" +license = "Elastic License v2" +name = "Potential Credential Discovery via Recursive Grep" +note = """## Triage and analysis + +### Investigating Potential Credential Discovery via Recursive Grep + +Adversaries and insider threats sometimes use `grep -r` (or `--recursive`, `-R`) across directories to find passwords, +API keys, private keys, cloud tokens, or repository and environment files. This rule looks for `grep`/`egrep` process +starts with recursive flags and command-line patterns associated with credential and secret discovery, then requires +**three or more distinct command lines** in the same one-minute bucket per host, user, and parent process. + +### Possible investigation steps + +- Review **Esql.cmd_values** for the exact patterns searched (paths, regex, file globs). +- Inspect **Esql.pcmd_values** and **process.parent.name** to see the launch context (interactive shell, script, IDE, CI). +- Confirm whether the user and host normally run security scans, audits, or developer tooling that legitimately greps for secrets. +- If suspicious, search the same host for file access, archive exfiltration, or cloud API use in the surrounding timeframe. + +### False positive analysis + +- Security scanners, secret scanners (e.g. in CI), and compliance scripts may match. Tune by **parent process**, **user**, + **working directory**, or organizational allowlists. +- Legitimate searches in documentation for the word "password" can match; the **unique_cmd >= 3** threshold reduces but + does not eliminate this. + +### Response and remediation + +- If unauthorized: contain the host, reset or rotate any credentials that may have been exposed, and review VCS and + cloud audit logs for follow-on abuse. +""" +references = [ + "https://attack.mitre.org/techniques/T1552/001/", + "https://attack.mitre.org/techniques/T1083/", +] +risk_score = 73 +rule_id = "b8e4c2a1-7f3d-4e9b-8c5a-1d0e6f2a4b8c" +severity = "high" +tags = [ + "Domain: Endpoint", + "OS: Linux", + "OS: macOS", + "Use Case: Threat Detection", + "Tactic: Credential Access", + "Tactic: Discovery", + "Resources: Investigation Guide", + "Data Source: Elastic Defend", +] +timestamp_override = "event.ingested" +type = "esql" + +query = ''' +from logs-endpoint.events.process-* metadata _id, _version, _index +| where host.os.type in ("linux", "macos") + and event.category == "process" + and process.name in ("grep", "egrep") + and (to_lower(process.command_line) like "* -r*" or to_lower(process.command_line) like "*--recursive*") + and ( + process.command_line like "*password*" + or process.command_line like "*passwd*" + or process.command_line like "*pwd*" + or process.command_line like "*secret*" + or process.command_line like "*token*" + or process.command_line like "*apikey*" + or process.command_line like "*api_key*" + or process.command_line like "*api.key*" + or process.command_line like "*access_key*" + or process.command_line like "*private_key*" + or process.command_line like "*client_secret*" + or process.command_line like "*credential*" + or process.command_line like "*auth*" + or process.command_line like "*bearer*" + or process.command_line like "*BEGIN*PRIVATE*KEY*" + or process.command_line like "*ssh-rsa*" + or process.command_line like "*ghp_*" + or process.command_line like "*github_pat*" + or process.command_line like "*xoxb-*" + or process.command_line like "*hooks.slack.com*" + or process.command_line like "*discord.com/api/webhooks*" + or process.command_line like "*/.aws/*" + or process.command_line like "*/.git/*" + or process.command_line like "*/.env*" + ) + and (process.parent.command_line is null or not (to_lower(process.parent.command_line) like "*shell-snapshots*" and process.parent.name in ("bash", "sh", "zsh"))) +| eval Esql.time_bucket = date_trunc(1 minute, @timestamp) +| stats Esql.unique_cmd = count_distinct(process.command_line), + Esql.cmd_values = values(process.command_line), + Esql.pcmd_values = values(process.parent.command_line) + by process.name, host.id, host.name, agent.id, process.parent.name, user.name, Esql.time_bucket +| where Esql.unique_cmd >= 3 +| keep host.id, host.name, agent.id, user.name, process.parent.name, Esql.* +''' + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1552" +name = "Unsecured Credentials" +reference = "https://attack.mitre.org/techniques/T1552/" +[[rule.threat.technique.subtechnique]] +id = "T1552.001" +name = "Credentials In Files" +reference = "https://attack.mitre.org/techniques/T1552/001/" + +[rule.threat.tactic] +id = "TA0006" +name = "Credential Access" +reference = "https://attack.mitre.org/tactics/TA0006/" + +[[rule.threat]] +framework = "MITRE ATT&CK" +[[rule.threat.technique]] +id = "T1083" +name = "File and Directory Discovery" +reference = "https://attack.mitre.org/techniques/T1083/" + +[rule.threat.tactic] +id = "TA0007" +name = "Discovery" +reference = "https://attack.mitre.org/tactics/TA0007/"