Potential Credential Discovery via Recursive Grep

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.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/03/25"
  3integration = ["endpoint"]
  4maturity = "production"
  5updated_date = "2026/03/25"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies recursive grep activity on Linux or macOS where the command line suggests hunting for secrets, credentials,
 11keys, tokens, or sensitive paths (for example .env, .git, .aws). Events are aggregated per host, user, parent process,
 12and one-minute window, the rule surfaces activity only when at least three distinct grep command lines match in the same
 13bucket, to reduce noise from one-off searches.
 14"""
 15from = "now-9m"
 16interval = "5m"
 17language = "esql"
 18license = "Elastic License v2"
 19name = "Potential Credential Discovery via Recursive Grep"
 20note = """## Triage and analysis
 21
 22### Investigating Potential Credential Discovery via Recursive Grep
 23
 24Adversaries and insider threats sometimes use `grep -r` (or `--recursive`, `-R`) across directories to find passwords,
 25API keys, private keys, cloud tokens, or repository and environment files. This rule looks for `grep`/`egrep` process
 26starts with recursive flags and command-line patterns associated with credential and secret discovery, then requires
 27**three or more distinct command lines** in the same one-minute bucket per host, user, and parent process.
 28
 29### Possible investigation steps
 30
 31- Review **Esql.cmd_values** for the exact patterns searched (paths, regex, file globs).
 32- Inspect **Esql.pcmd_values** and **process.parent.name** to see the launch context (interactive shell, script, IDE, CI).
 33- Confirm whether the user and host normally run security scans, audits, or developer tooling that legitimately greps for secrets.
 34- If suspicious, search the same host for file access, archive exfiltration, or cloud API use in the surrounding timeframe.
 35
 36### False positive analysis
 37
 38- Security scanners, secret scanners (e.g. in CI), and compliance scripts may match. Tune by **parent process**, **user**,
 39  **working directory**, or organizational allowlists.
 40- Legitimate searches in documentation for the word "password" can match; the **unique_cmd >= 3** threshold reduces but
 41  does not eliminate this.
 42
 43### Response and remediation
 44
 45- If unauthorized: contain the host, reset or rotate any credentials that may have been exposed, and review VCS and
 46  cloud audit logs for follow-on abuse.
 47"""
 48references = [
 49    "https://attack.mitre.org/techniques/T1552/001/",
 50    "https://attack.mitre.org/techniques/T1083/",
 51]
 52risk_score = 73
 53rule_id = "b8e4c2a1-7f3d-4e9b-8c5a-1d0e6f2a4b8c"
 54severity = "high"
 55tags = [
 56    "Domain: Endpoint",
 57    "OS: Linux",
 58    "OS: macOS",
 59    "Use Case: Threat Detection",
 60    "Tactic: Credential Access",
 61    "Tactic: Discovery",
 62    "Resources: Investigation Guide",
 63    "Data Source: Elastic Defend",
 64]
 65timestamp_override = "event.ingested"
 66type = "esql"
 67
 68query = '''
 69from logs-endpoint.events.process-* metadata _id, _version, _index
 70| where host.os.type in ("linux", "macos")
 71  and event.category == "process"
 72  and process.name in ("grep", "egrep")
 73  and (to_lower(process.command_line) like "* -r*" or to_lower(process.command_line) like "*--recursive*")
 74  and (
 75    process.command_line like "*password*"
 76    or process.command_line like "*passwd*"
 77    or process.command_line like "*pwd*"
 78    or process.command_line like "*secret*"
 79    or process.command_line like "*token*"
 80    or process.command_line like "*apikey*"
 81    or process.command_line like "*api_key*"
 82    or process.command_line like "*api.key*"
 83    or process.command_line like "*access_key*"
 84    or process.command_line like "*private_key*"
 85    or process.command_line like "*client_secret*"
 86    or process.command_line like "*credential*"
 87    or process.command_line like "*auth*"
 88    or process.command_line like "*bearer*"
 89    or process.command_line like "*BEGIN*PRIVATE*KEY*"
 90    or process.command_line like "*ssh-rsa*"
 91    or process.command_line like "*ghp_*"
 92    or process.command_line like "*github_pat*"
 93    or process.command_line like "*xoxb-*"
 94    or process.command_line like "*hooks.slack.com*"
 95    or process.command_line like "*discord.com/api/webhooks*"
 96    or process.command_line like "*/.aws/*"
 97    or process.command_line like "*/.git/*"
 98    or process.command_line like "*/.env*"
 99  )
100  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")))
101| eval Esql.time_bucket = date_trunc(1 minute, @timestamp)
102| stats Esql.unique_cmd = count_distinct(process.command_line),
103        Esql.cmd_values = values(process.command_line),
104        Esql.pcmd_values = values(process.parent.command_line)
105  by process.name, host.id, host.name, agent.id, process.parent.name, user.name, Esql.time_bucket
106| where Esql.unique_cmd >= 3
107| keep host.id, host.name, agent.id, user.name, process.parent.name, Esql.*
108'''
109
110[[rule.threat]]
111framework = "MITRE ATT&CK"
112[[rule.threat.technique]]
113id = "T1552"
114name = "Unsecured Credentials"
115reference = "https://attack.mitre.org/techniques/T1552/"
116[[rule.threat.technique.subtechnique]]
117id = "T1552.001"
118name = "Credentials In Files"
119reference = "https://attack.mitre.org/techniques/T1552/001/"
120
121[rule.threat.tactic]
122id = "TA0006"
123name = "Credential Access"
124reference = "https://attack.mitre.org/tactics/TA0006/"
125
126[[rule.threat]]
127framework = "MITRE ATT&CK"
128[[rule.threat.technique]]
129id = "T1083"
130name = "File and Directory Discovery"
131reference = "https://attack.mitre.org/techniques/T1083/"
132
133[rule.threat.tactic]
134id = "TA0007"
135name = "Discovery"
136reference = "https://attack.mitre.org/tactics/TA0007/"

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

Related rules

to-top