Multi-Cloud CLI Token and Credential Access Commands

Correlates process telemetry for shells and major cloud/Kubernetes CLIs when command lines match token or credential material access patterns (GCP, Azure, AWS, GitHub, kubectl, DigitalOcean, OCI). Flags hosts where multiple cloud targets appear within a five-minute window.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/04/29"
  3integration = ["endpoint", "windows", "system"]
  4maturity = "production"
  5updated_date = "2026/04/29"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Correlates process telemetry for shells and major cloud/Kubernetes CLIs when command lines match token or credential
 11material access patterns (GCP, Azure, AWS, GitHub, kubectl, DigitalOcean, OCI). Flags hosts where multiple cloud
 12targets appear within a five-minute window.
 13"""
 14false_positives = [
 15    """
 16    Automation, CI runners, and platform engineering scripts may legitimately print tokens or dump kubeconfig across
 17    providers in one session. Baseline approved identities and runner images before tuning thresholds.
 18    """,
 19]
 20from = "now-6m"
 21language = "esql"
 22license = "Elastic License v2"
 23name = "Multi-Cloud CLI Token and Credential Access Commands"
 24note = """## Triage and analysis
 25
 26### Investigating Multi-Cloud CLI Token and Credential Access Commands
 27
 28Each result row summarizes activity for one host, user, and five-minute time bucket. Review `Esql.process_command_line_values` for the
 29exact invocations and confirm whether the session was interactive, automated, or tied to a known pipeline.
 30
 31### Possible investigation steps
 32
 33- Map `Esql.cloud_targets` and `Esql.unique_clouds` to the underlying `process.command_line` values and parent
 34  executables.
 35- Correlate with authentication, Kubernetes audit, and cloud API logs for misuse of printed tokens.
 36- Identify whether the parent chain indicates a remote shell, RMM, or scheduled task.
 37
 38### Response and remediation
 39
 40- If unauthorized, isolate the host, invalidate any printed material at the identity provider, and hunt for lateral
 41  movement using the same time window as the alert.
 42
 43**GCP (gcloud / application-default credentials)**
 44
 45- Sign the user or build identity out of local gcloud sessions on the affected machine (example host session):
 46
 47  `gcloud auth revoke --all`
 48
 49- Remove leaked Application Default Credentials on that host (often used by client libraries):
 50
 51  `gcloud auth application-default revoke`
 52
 53- If a user OAuth refresh token or service account key was exposed, revoke or rotate it in Google Cloud Console (IAM
 54  and admin: delete compromised keys; for end users, revoke OAuth tokens under Security or Workspace admin tools as
 55  applicable).
 56
 57**Azure (`az` / `azd`)**
 58
 59- Clear cached CLI sessions on the host so new tokens are not silently reusable from disk:
 60
 61  `az logout`
 62
 63  `az account clear`
 64
 65- If `az account get-access-token`, `Get-AzAccessToken`, or `azd auth token` output was captured, treat the bearer as
 66  compromised: rotate the underlying secret (for example app registration client secret or federated credential),
 67  revoke sessions in Microsoft Entra ID where supported, and enforce re-authentication with Conditional Access.
 68
 69**GitHub (`gh` / PATs)**
 70
 71- Remove the GitHub CLI session from the affected profile:
 72
 73  `gh auth logout`
 74
 75- If a personal access token or fine-grained token was printed, revoke it under GitHub user or organization settings
 76  (Developer settings → Personal access tokens), and rotate any secrets or deploy keys that were readable with that
 77  token.
 78
 79For all providers, prefer provider-console revocation and rotation when a token string left the trust boundary; local
 80`logout`/`revoke` alone does not invalidate tokens that were already copied off-host.
 81"""
 82references = [
 83    "https://attack.mitre.org/techniques/T1528/",
 84    "https://attack.mitre.org/techniques/T1552/",
 85]
 86risk_score = 73
 87rule_id = "2b9a3b7a-0891-4a89-abbe-dca753c403cd"
 88severity = "high"
 89tags = [
 90    "Domain: Endpoint",
 91    "Domain: Cloud",
 92    "OS: Windows",
 93    "OS: Linux",
 94    "OS: macOS",
 95    "Use Case: Threat Detection",
 96    "Tactic: Credential Access",
 97    "Data Source: Elastic Defend",
 98    "Data Source: Windows Security Event Logs",
 99    "Data Source: Sysmon",
100    "Resources: Investigation Guide"
101]
102timestamp_override = "event.ingested"
103type = "esql"
104query = '''
105FROM logs-endpoint.events.process-*, logs-system.security-*, logs-windows.sysmon_operational-* METADATA _id, _index, _version
106| WHERE event.category == "process" AND KQL(""" event.type : "start" and not event.action : "fork" """)
107  AND process.command_line IS NOT NULL 
108  AND (
109    TO_LOWER(process.name) IN (
110      "cmd.exe", "powershell.exe", "pwsh.exe", 
111      "sh", "bash", "zsh", "dash", "fish", "ksh",
112      "gcloud", "gcloud.cmd", "az", "az.cmd", "azd", "azd.exe",
113      "gh", "gh.exe", "aws", "aws.exe",
114      "kubectl", "kubectl.exe",
115      "doctl", "doctl.exe",
116      "oci", "oci.exe"
117    ) OR
118    TO_LOWER(process.parent.name) IN (
119      "cmd.exe", "powershell.exe", "pwsh.exe",
120      "sh", "bash", "zsh", "dash", "fish", "ksh", "bun", "bun.exe", 
121      "node", "node.exe", "java", "java.exe"
122    )
123  )
124  AND process.command_line RLIKE """.*(config-helper\s.*--format|auth\s+print-access-token|auth\s+print-identity-token|auth\s+application-default\s+print|get-access-token\s.*--output|Get-AzAccessToken|azd\s+auth\s+token|az\s+account\s+get-access-token|gh\s+auth\s+(token|status)|aws\s+sts\s+(get-session-token|get-caller-identity|assume-role)|aws\s+configure\s+(export-credentials|list)|kubectl\s+config\s+view\s.*--raw|kubectl\s+get\s+secret|doctl\s+auth\s+(list|init)|oci\s+session\s+authenticate|oci\s+iam\s.*token).*"""
125| EVAL cloud_target = CASE(
126    process.command_line RLIKE ".*(gcloud|config-helper|print-access-token|print-identity-token).*", "GCP",
127    process.command_line RLIKE ".*(azd auth|az account|Get-AzAccessToken).*", "AZURE",
128    process.command_line RLIKE ".*(aws sts|aws configure).*", "AWS",
129    process.command_line RLIKE ".*(gh auth).*", "GITHUB",
130    process.command_line RLIKE ".*(kubectl config|kubectl get secret).*", "KUBERNETES",
131    process.command_line RLIKE ".*(doctl).*", "DIGITALOCEAN",
132    process.command_line RLIKE ".*(oci session|oci iam).*", "ORACLE"
133  )
134| WHERE cloud_target IS NOT NULL // drop unclassified events before aggregation
135| STATS
136    Esql.cloud_targets = VALUES(cloud_target),
137    Esql.unique_clouds = COUNT_DISTINCT(cloud_target),
138    Esql.process_command_line_values = VALUES(process.command_line),
139    Esql.process_parent_executable_values = VALUES(process.parent.executable),
140    Esql.first_seen = MIN(@timestamp),
141    Esql.last_seen = MAX(@timestamp),
142    Esql.event_count = COUNT(*)
143  BY host.name, host.id, user.name
144| WHERE Esql.unique_clouds >= 2
145| KEEP Esql.*, user.name, host.name, host.id
146'''
147
148[[rule.threat]]
149framework = "MITRE ATT&CK"
150
151[[rule.threat.technique]]
152id = "T1528"
153name = "Steal Application Access Token"
154reference = "https://attack.mitre.org/techniques/T1528/"
155
156[[rule.threat.technique]]
157id = "T1552"
158name = "Unsecured Credentials"
159reference = "https://attack.mitre.org/techniques/T1552/"
160
161[[rule.threat.technique.subtechnique]]
162id = "T1552.001"
163name = "Credentials In Files"
164reference = "https://attack.mitre.org/techniques/T1552/001/"
165
166[rule.threat.tactic]
167id = "TA0006"
168name = "Credential Access"
169reference = "https://attack.mitre.org/tactics/TA0006/"

Triage and analysis

Investigating Multi-Cloud CLI Token and Credential Access Commands

Each result row summarizes activity for one host, user, and five-minute time bucket. Review Esql.process_command_line_values for the exact invocations and confirm whether the session was interactive, automated, or tied to a known pipeline.

Possible investigation steps

  • Map Esql.cloud_targets and Esql.unique_clouds to the underlying process.command_line values and parent executables.
  • Correlate with authentication, Kubernetes audit, and cloud API logs for misuse of printed tokens.
  • Identify whether the parent chain indicates a remote shell, RMM, or scheduled task.

Response and remediation

  • If unauthorized, isolate the host, invalidate any printed material at the identity provider, and hunt for lateral movement using the same time window as the alert.

GCP (gcloud / application-default credentials)

  • Sign the user or build identity out of local gcloud sessions on the affected machine (example host session):

    gcloud auth revoke --all

  • Remove leaked Application Default Credentials on that host (often used by client libraries):

    gcloud auth application-default revoke

  • If a user OAuth refresh token or service account key was exposed, revoke or rotate it in Google Cloud Console (IAM and admin: delete compromised keys; for end users, revoke OAuth tokens under Security or Workspace admin tools as applicable).

Azure (az / azd)

  • Clear cached CLI sessions on the host so new tokens are not silently reusable from disk:

    az logout

    az account clear

  • If az account get-access-token, Get-AzAccessToken, or azd auth token output was captured, treat the bearer as compromised: rotate the underlying secret (for example app registration client secret or federated credential), revoke sessions in Microsoft Entra ID where supported, and enforce re-authentication with Conditional Access.

GitHub (gh / PATs)

  • Remove the GitHub CLI session from the affected profile:

    gh auth logout

  • If a personal access token or fine-grained token was printed, revoke it under GitHub user or organization settings (Developer settings → Personal access tokens), and rotate any secrets or deploy keys that were readable with that token.

For all providers, prefer provider-console revocation and rotation when a token string left the trust boundary; local logout/revoke alone does not invalidate tokens that were already copied off-host.

References

Related rules

to-top