Package Runtime Secret File Access Followed by IMDS Request

Identifies a Python, Node.js, Bun, Deno, Ruby, PHP or Perl runtime, or a binary executing from a package install location, that opens a developer secret (SSH keys, package registry tokens, source control credentials or AI coding assistant credentials) and then connects to a cloud instance metadata service (IMDS) endpoint within one minute. This sequence matches credential stealers delivered through compromised packages, such as the malicious LiteLLM PyPI releases, while cloud provider credential files are excluded because legitimate cloud SDK credential chains read them before falling back to IMDS.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/10/23"
  3integration = ["endpoint"]
  4maturity = "production"
  5updated_date = "2026/10/23"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies a Python, Node.js, Bun, Deno, Ruby, PHP or Perl runtime, or a binary executing from a package install
 11location, that opens a developer secret (SSH keys, package registry tokens, source control credentials or AI coding
 12assistant credentials) and then connects to a cloud instance metadata service (IMDS) endpoint within one minute. This
 13sequence matches credential stealers delivered through compromised packages, such as the malicious LiteLLM PyPI
 14releases, while cloud provider credential files are excluded because legitimate cloud SDK credential chains read them
 15before falling back to IMDS.
 16"""
 17false_positives = [
 18    """
 19    Developer or deployment scripts running on cloud-hosted macOS or Windows instances may read a package registry or
 20    source control token and then request instance role credentials from the same interpreter (for example a release
 21    script that publishes a package and uploads artifacts with the instance role). Validate the script path, parent
 22    process and whether the host is an approved build or release system.
 23    """,
 24    """
 25    AI coding assistants running on cloud instances may read their own credential file and then fall back to the
 26    instance metadata service for cloud provider authentication (for example Gemini CLI on Google Compute Engine).
 27    Confirm the process is the assistant itself and that the credential file belongs to that assistant.
 28    """,
 29]
 30from = "now-9m"
 31index = ["logs-endpoint.events.file-*", "logs-endpoint.events.network-*"]
 32language = "eql"
 33license = "Elastic License v2"
 34name = "Package Runtime Secret File Access Followed by IMDS Request"
 35note = """## Triage and analysis
 36
 37### Investigating Package Runtime Secret File Access Followed by IMDS Request
 38
 39This rule correlates two behaviors from the same package ecosystem runtime process (`process.entity_id`) within one
 40minute: opening a local secret store that cloud SDKs never read, followed by a connection to a cloud instance metadata endpoint
 41(`169.254.169.254`, the ECS and EKS Pod Identity credential endpoints, the AWS IPv6 IMDS endpoints, or the Alibaba
 42Cloud metadata endpoint). Legitimate software rarely needs both an SSH key, registry token or AI assistant credential
 43and cloud role credentials. Infostealers delivered through compromised packages routinely do both, typically from a
 44Python or Node.js interpreter that imported the malicious package.
 45
 46### Possible investigation steps
 47
 48- Identify the process (`process.executable`, `process.entity_id`) and walk its ancestry to determine what launched it.
 49  Pay close attention to package managers, CI runners, notebooks, IDEs, AI coding agents, and interpreters executing
 50  from virtual environments or `site-packages`/`node_modules`.
 51- Review all file events for the same `process.entity_id` to scope which secrets were accessed. Stealers usually
 52  enumerate many credential locations in a short burst.
 53- Review all network events for the same `process.entity_id` for outbound connections to non-metadata destinations
 54  immediately after the IMDS request, which may indicate exfiltration of the harvested data.
 55- Look for Python `.pth` files, `sitecustomize.py`/`usercustomize.py`, or recently installed or upgraded packages in the
 56  interpreter's environment that could have executed the code at import time.
 57- Check cloud audit logs (CloudTrail, GCP Audit, Azure Activity) for use of the instance role credentials from IP
 58  addresses outside the instance's expected egress.
 59
 60### False positive analysis
 61
 62- Release or deployment scripts on cloud-hosted build agents may read a registry or source control token and then
 63  request instance role credentials from the same interpreter. Confirm the script is an approved part of a build or
 64  release pipeline on that host.
 65- AI coding assistants on cloud instances may read their own credential file before falling back to the metadata server
 66  for cloud authentication. Confirm the process is the assistant that owns the credential file.
 67
 68### Response and remediation
 69
 70- Isolate the host and terminate the offending process tree.
 71- Rotate every secret the process opened (SSH keys, npm/PyPI tokens, Git and GitHub CLI tokens, Vault and Terraform
 72  tokens, AI assistant OAuth tokens) and revoke active sessions for the instance role or workload identity.
 73- Identify and remove the malicious package version from the environment, lock files and internal mirrors, and hunt for
 74  the same package version across other hosts and CI runners.
 75- Enforce IMDSv2 with a hop limit of 1 where possible and scope instance roles to least privilege.
 76"""
 77risk_score = 73
 78rule_id = "2baf0a3c-50cd-43b1-9e9a-d7ff3153b36b"
 79setup = """## Setup
 80
 81This rule requires data from Elastic Defend with file and network event collection enabled.
 82
 83Elastic Defend only generates file `open` events on macOS and Windows, and only for paths in its event-on-access file
 84path list. That list is delivered through the Elastic Defend global configuration artifact and can be extended per
 85policy with the `advanced.events.event_on_access.file_paths` advanced setting. Credential paths in this rule that are
 86not yet present in the deployed global configuration only match after they are added to that setting. On Windows,
 87repeated access events for the same path are deduplicated for one hour by default.
 88
 89This rule is scoped to macOS and Windows because Elastic Defend does not generate file `open` events on Linux.
 90"""
 91severity = "high"
 92tags = [
 93    "Domain: Endpoint",
 94    "Domain: Cloud",
 95    "OS: macOS",
 96    "OS: Windows",
 97    "Use Case: Threat Detection",
 98    "Tactic: Credential Access",
 99    "Tactic: Initial Access",
100    "Data Source: Elastic Defend",
101    "Resources: Investigation Guide",
102    "Threat: Supply Chain",
103    "Threat: IMDS Credential Theft",
104    "Rule Type: Event Correlation (EQL)",
105    "Platform: macOS",
106    "Platform: Windows",
107    "Domain: GenAI",
108    "Mitre Atlas: AML.T0010",
109    "Mitre Atlas: AML.T0010.001",
110    "Mitre Atlas: AML.T0055",
111    "Mitre Atlas: AML.T0083",
112]
113type = "eql"
114
115query = '''
116sequence by process.entity_id with maxspan=1m
117  [file where event.action == "open" and host.os.type in ("macos", "windows") and
118    file.path like~ (
119      /* SSH private keys */
120      "/Users/*/.ssh/id_*", "?:\\Users\\*\\.ssh\\id_*",
121
122      /* Package registry, source control and secrets manager tokens */
123      "/Users/*/.npmrc", "?:\\Users\\*\\.npmrc",
124      "/Users/*/.pypirc", "?:\\Users\\*\\.pypirc",
125      "/Users/*/.git-credentials", "?:\\Users\\*\\.git-credentials",
126      "/Users/*/.config/git/credentials", "?:\\Users\\*\\.config\\git\\credentials",
127      "/Users/*/.config/gh/hosts.yml", "?:\\Users\\*\\AppData\\Roaming\\GitHub CLI\\hosts.yml",
128      "/Users/*/.vault-token", "?:\\Users\\*\\.vault-token",
129      "/Users/*/.terraform.d/credentials.tfrc.json", "?:\\Users\\*\\AppData\\Roaming\\terraform.d\\credentials.tfrc.json",
130
131      /* Additional package registry and AI/ML platform tokens */
132      "/Users/*/.yarnrc.yml", "?:\\Users\\*\\.yarnrc.yml",
133      "/Users/*/.gem/credentials", "?:\\Users\\*\\.gem\\credentials",
134      "/Users/*/.cargo/credentials.toml", "?:\\Users\\*\\.cargo\\credentials.toml",
135      "/Users/*/.composer/auth.json", "/Users/*/.config/composer/auth.json", "?:\\Users\\*\\AppData\\Roaming\\Composer\\auth.json",
136      "/Users/*/.cache/huggingface/token", "/Users/*/.huggingface/token", "?:\\Users\\*\\.cache\\huggingface\\token", "?:\\Users\\*\\.huggingface\\token",
137      "/Users/*/.config/github-copilot/apps.json", "/Users/*/.config/github-copilot/hosts.json",
138      "?:\\Users\\*\\AppData\\Local\\github-copilot\\apps.json", "?:\\Users\\*\\AppData\\Local\\github-copilot\\hosts.json",
139      "/Users/*/.ollama/id_ed25519", "?:\\Users\\*\\.ollama\\id_ed25519",
140
141      /* AI coding assistant credentials */
142      "/Users/*/.claude/.credentials.json", "?:\\Users\\*\\.claude\\.credentials.json",
143      "/Users/*/.codex/auth.json", "?:\\Users\\*\\.codex\\auth.json",
144      "/Users/*/.codex/.credentials.json", "?:\\Users\\*\\.codex\\.credentials.json",
145      "/Users/*/.codex/secrets/*.age", "?:\\Users\\*\\.codex\\secrets\\*.age",
146      "/Users/*/.gemini/oauth_creds.json", "?:\\Users\\*\\.gemini\\oauth_creds.json",
147      "/Users/*/.gemini/gemini-credentials.json", "?:\\Users\\*\\.gemini\\gemini-credentials.json",
148      "/Users/*/.gemini/mcp-oauth-tokens.json", "?:\\Users\\*\\.gemini\\mcp-oauth-tokens.json",
149      "/Users/*/.gemini/a2a-oauth-tokens.json", "?:\\Users\\*\\.gemini\\a2a-oauth-tokens.json",
150      "/Users/*/.qwen/oauth_creds.json", "?:\\Users\\*\\.qwen\\oauth_creds.json",
151      "/Users/*/.qwen/mcp-oauth-tokens.json", "?:\\Users\\*\\.qwen\\mcp-oauth-tokens.json",
152      "/Users/*/.aws/sso/cache/kiro-auth-token.json", "?:\\Users\\*\\.aws\\sso\\cache\\kiro-auth-token.json",
153      "/Users/*/.local/share/opencode/auth.json", "?:\\Users\\*\\.local\\share\\opencode\\auth.json",
154      "/Users/*/.local/share/opencode/mcp-auth.json", "?:\\Users\\*\\.local\\share\\opencode\\mcp-auth.json",
155      "/Users/*/.local/share/amp/secrets.json", "?:\\Users\\*\\.local\\share\\amp\\secrets.json",
156      "/Users/*/.augment/session.json", "?:\\Users\\*\\.augment\\session.json",
157      "/Users/*/.kimi/credentials/kimi-code.json", "?:\\Users\\*\\.kimi\\credentials\\kimi-code.json",
158      "/Users/*/.cursor/auth.json", "?:\\Users\\*\\.cursor\\auth.json",
159      "/Users/*/.pi/agent/auth.json", "?:\\Users\\*\\.pi\\agent\\auth.json",
160      "/Users/*/.config/goose/secrets.yaml", "?:\\Users\\*\\AppData\\Roaming\\Block\\goose\\config\\secrets.yaml",
161      "/Users/*/.config/muse/auth.json", "?:\\Users\\*\\.config\\muse\\auth.json",
162      "/Users/*/.local/share/devin/credentials.toml", "?:\\Users\\*\\AppData\\Roaming\\devin\\credentials.toml",
163      "/Users/*/.openclaw/credentials/oauth.json", "?:\\Users\\*\\.openclaw\\credentials\\oauth.json",
164      "/Users/*/.openclaw/agents/*/agent/auth-profiles.json", "?:\\Users\\*\\.openclaw\\agents\\*\\agent\\auth-profiles.json",
165      "/Users/*/.openclaw/agents/*/agent/auth.json", "?:\\Users\\*\\.openclaw\\agents\\*\\agent\\auth.json"
166    ) and
167
168    /* SSH public keys are not secrets and are routinely read by SSH and cloud tooling */
169    not file.path like~ "*.pub" and
170
171    /* Package ecosystem runtimes or binaries executing from package install locations */
172    (
173      process.name like~ ("python*", "node", "node.exe", "nodejs", "bun", "bun.exe", "deno", "deno.exe", "ruby*", "php*", "perl*") or
174      process.executable like~ ("*/site-packages/*", "*/dist-packages/*", "*/node_modules/*", "*\\site-packages\\*", "*\\node_modules\\*",
175                                "*/.venv/*", "*/venv/*", "*\\.venv\\*", "*\\venv\\*")
176    )
177  ]
178  [network where
179    cidrmatch(destination.ip, "169.254.169.254/32", "169.254.170.2/32", "169.254.170.23/32", "100.100.100.200/32", "fd00:ec2::254/128", "fd00:ec2::23/128") and
180    destination.port == 80 and event.action == "connection_attempted" and host.os.type in ("macos", "windows")
181  ]
182'''
183
184
185[[rule.threat]]
186framework = "MITRE ATT&CK"
187[[rule.threat.technique]]
188id = "T1552"
189name = "Unsecured Credentials"
190reference = "https://attack.mitre.org/techniques/T1552/"
191[[rule.threat.technique.subtechnique]]
192id = "T1552.001"
193name = "Credentials In Files"
194reference = "https://attack.mitre.org/techniques/T1552/001/"
195
196[[rule.threat.technique.subtechnique]]
197id = "T1552.004"
198name = "Private Keys"
199reference = "https://attack.mitre.org/techniques/T1552/004/"
200
201[[rule.threat.technique.subtechnique]]
202id = "T1552.005"
203name = "Cloud Instance Metadata API"
204reference = "https://attack.mitre.org/techniques/T1552/005/"
205
206
207
208[rule.threat.tactic]
209id = "TA0006"
210name = "Credential Access"
211reference = "https://attack.mitre.org/tactics/TA0006/"
212[[rule.threat]]
213framework = "MITRE ATT&CK"
214[[rule.threat.technique]]
215id = "T1195"
216name = "Supply Chain Compromise"
217reference = "https://attack.mitre.org/techniques/T1195/"
218[[rule.threat.technique.subtechnique]]
219id = "T1195.001"
220name = "Compromise Software Dependencies and Development Tools"
221reference = "https://attack.mitre.org/techniques/T1195/001/"
222
223
224
225[rule.threat.tactic]
226id = "TA0001"
227name = "Initial Access"
228reference = "https://attack.mitre.org/tactics/TA0001/"
229[[rule.threat_mappings]]
230framework = "MITRE ATLAS"
231version = "2026.08"
232[[rule.threat_mappings.threat]]
233framework = "MITRE ATLAS"
234[[rule.threat_mappings.threat.technique]]
235id = "AML.T0010"
236name = "AI Supply Chain Compromise"
237reference = "https://atlas.mitre.org/techniques/AML.T0010/"
238[[rule.threat_mappings.threat.technique.subtechnique]]
239id = "AML.T0010.001"
240name = "AI Software"
241reference = "https://atlas.mitre.org/techniques/AML.T0010.001/"
242
243
244
245[rule.threat_mappings.threat.tactic]
246id = "AML.TA0004"
247name = "Initial Access"
248reference = "https://atlas.mitre.org/tactics/AML.TA0004/"
249[[rule.threat_mappings.threat]]
250framework = "MITRE ATLAS"
251[[rule.threat_mappings.threat.technique]]
252id = "AML.T0055"
253name = "Unsecured Credentials"
254reference = "https://atlas.mitre.org/techniques/AML.T0055/"
255
256[[rule.threat_mappings.threat.technique]]
257id = "AML.T0083"
258name = "Credentials from AI Agent Configuration"
259reference = "https://atlas.mitre.org/techniques/AML.T0083/"
260
261
262[rule.threat_mappings.threat.tactic]
263id = "AML.TA0013"
264name = "Credential Access"
265reference = "https://atlas.mitre.org/tactics/AML.TA0013/"

Triage and analysis

Investigating Package Runtime Secret File Access Followed by IMDS Request

This rule correlates two behaviors from the same package ecosystem runtime process (process.entity_id) within one minute: opening a local secret store that cloud SDKs never read, followed by a connection to a cloud instance metadata endpoint (169.254.169.254, the ECS and EKS Pod Identity credential endpoints, the AWS IPv6 IMDS endpoints, or the Alibaba Cloud metadata endpoint). Legitimate software rarely needs both an SSH key, registry token or AI assistant credential and cloud role credentials. Infostealers delivered through compromised packages routinely do both, typically from a Python or Node.js interpreter that imported the malicious package.

Possible investigation steps

  • Identify the process (process.executable, process.entity_id) and walk its ancestry to determine what launched it. Pay close attention to package managers, CI runners, notebooks, IDEs, AI coding agents, and interpreters executing from virtual environments or site-packages/node_modules.
  • Review all file events for the same process.entity_id to scope which secrets were accessed. Stealers usually enumerate many credential locations in a short burst.
  • Review all network events for the same process.entity_id for outbound connections to non-metadata destinations immediately after the IMDS request, which may indicate exfiltration of the harvested data.
  • Look for Python .pth files, sitecustomize.py/usercustomize.py, or recently installed or upgraded packages in the interpreter's environment that could have executed the code at import time.
  • Check cloud audit logs (CloudTrail, GCP Audit, Azure Activity) for use of the instance role credentials from IP addresses outside the instance's expected egress.

False positive analysis

  • Release or deployment scripts on cloud-hosted build agents may read a registry or source control token and then request instance role credentials from the same interpreter. Confirm the script is an approved part of a build or release pipeline on that host.
  • AI coding assistants on cloud instances may read their own credential file before falling back to the metadata server for cloud authentication. Confirm the process is the assistant that owns the credential file.

Response and remediation

  • Isolate the host and terminate the offending process tree.
  • Rotate every secret the process opened (SSH keys, npm/PyPI tokens, Git and GitHub CLI tokens, Vault and Terraform tokens, AI assistant OAuth tokens) and revoke active sessions for the instance role or workload identity.
  • Identify and remove the malicious package version from the environment, lock files and internal mirrors, and hunt for the same package version across other hosts and CI runners.
  • Enforce IMDSv2 with a hop limit of 1 where possible and scope instance roles to least privilege.

Related rules

to-top