GenAI Agent Instruction File Written to Downloads


Description

Detects creation or change of an AI coding-agent instruction file (agents.md or claude.md) in a user Downloads folder by a browser or an archive tool. A web page can save AGENTS.md or CLAUDE.md into Downloads without a click, and opening an archive can extract the same files there. Codex and Claude Code treat those files as trusted project instructions, which can lead them to rewrite their own configuration, install a package, or leak a login token.

Query · esql

FROM logs-endpoint.events.file-* METADATA _id, _version, _index
| WHERE event.type IN ("creation", "change")
  AND TO_LOWER(file.name) IN ("agents.md", "claude.md")
  AND (
    file.path LIKE "/Users/*/Downloads/*"
    OR file.path LIKE "/home/*/Downloads/*"
    OR file.path LIKE """C:\\Users\\*\\Downloads\\*"""
  )
  AND (
    TO_LOWER(process.name) IN (
      "google chrome", "chrome.exe", "chrome", "chromium", "chromium-browser", "google-chrome",
      "microsoft edge", "msedge.exe", "microsoft-edge",
      "firefox", "firefox.exe",
      "safari",
      "brave browser", "brave.exe", "brave", "brave-browser",
      "opera", "opera.exe",
      "vivaldi", "vivaldi.exe",
      "explorer.exe",
      "com.apple.webkit.networking",
      "7z.exe", "7zg.exe", "7zfm.exe", "7z", "7zz",
      "winrar.exe", "rar.exe", "unrar.exe", "rar", "unrar",
      "winzip.exe", "winzip64.exe",
      "bandizip.exe", "peazip.exe",
      "tar.exe", "tar", "bsdtar",
      "unzip.exe", "unzip", "unar", "ditto",
      "archive utility", "bomarchivehelper",
      "the unarchiver", "keka",
      "file-roller", "ark"
    )
    OR TO_LOWER(process.name) LIKE (
      "google chrome helper*",
      "microsoft edge helper*",
      "brave browser helper*",
      "opera helper*",
      "vivaldi helper*"
    )
  )
| KEEP
    _id,
    _version,
    _index,
    data_stream.namespace,
    @timestamp,
    host.name,
    host.id,
    agent.id,
    user.name,
    user.domain,
    event.type,
    event.action,
    file.name,
    file.path,
    process.name,
    process.executable,
    process.entity_id

Investigation fields

Pivot points the source recommends for triage.

  • host.name
  • user.name
  • file.name
  • file.path
  • process.name
  • process.executable

Implementation guide

This rule is designed for data generated by Elastic Defend, which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.

Setup instructions: https://ela.st/install-elastic-defend

Analyst notes

Investigating GenAI Agent Instruction File Written to Downloads

AGENTS.md and CLAUDE.md are instruction files that coding agents such as OpenAI Codex and Claude Code read when a session starts in that folder and then follow. Downloads is a folder a browser can write to on its own, and it is also where archive tools extract files the user opened. A planted instruction file can tell the agent to register a malicious MCP server, install a package, or point its API traffic at an attacker-controlled gateway.

Possible investigation steps

  • Open file.path and read the file. Look for instructions that edit agent config (~/.codex/config.toml, .claude/settings.json, mcp.json), register an MCP server, run npx or npm install, or set ANTHROPIC_BASE_URL or a similar API base URL.
  • Review process.name and process.executable. A browser or browser helper means the file was saved from the web. An archive tool (7z, WinRAR, tar, unzip, Archive Utility) means it was extracted from a download.
  • On the same host and user, look for a preceding download of a zip, rar, or 7z into the same Downloads folder, and for follow-on writes to .codex, .claude, or other GenAI config paths.
  • Check whether Codex, Claude, or another agent was later started with that Downloads folder as its workspace, and whether it spawned npm, npx, node, or a shell afterward.
  • If the file is malicious, hunt the same filename and content hash on other hosts.

False positive analysis

  • Cloning or downloading a legitimate repository into Downloads extracts a real AGENTS.md or CLAUDE.md. Confirm the archive or URL and the file contents before excluding.
  • Saving those files from a browser while working on an agent project in Downloads is expected. Scope exceptions to a known path, user, or parent archive, not to the filename alone.

Response and remediation

  • Quarantine the instruction file and any archive it came from. Collect the hash and the download URL if present.
  • If an agent already consumed the file, review ~/.codex/config.toml, .claude/settings.json, and other MCP config for new servers or API base URLs, and remove unauthorized entries.
  • Rotate agent login tokens and any credentials the session could have read. Remove packages the agent installed as part of the instructions, such as an unexpected npm package.
Raw source GenAI Agent Instruction File Written to Downloads · Elastic TOML
Esc
Published by elastic/detection-rules ↗, licensed under Elastic License 2.0 ↗. Reproduced here unmodified.
[metadata]
creation_date = "2026/10/27"
integration = ["endpoint"]
maturity = "production"
updated_date = "2026/10/31"

[rule]
author = ["Elastic"]
description = """
Detects creation or change of an AI coding-agent instruction file (agents.md or claude.md) in a user Downloads
folder by a browser or an archive tool. A web page can save AGENTS.md or CLAUDE.md into Downloads without a click,
and opening an archive can extract the same files there. Codex and Claude Code treat those files as trusted project
instructions, which can lead them to rewrite their own configuration, install a package, or leak a login token.
"""
from = "now-9m"
language = "esql"
license = "Elastic License v2"
name = "GenAI Agent Instruction File Written to Downloads"
note = """## Triage and analysis

### Investigating GenAI Agent Instruction File Written to Downloads

`AGENTS.md` and `CLAUDE.md` are instruction files that coding agents such as OpenAI Codex and Claude Code read when
a session starts in that folder and then follow. Downloads is a folder a browser can write to on its own, and it is
also where archive tools extract files the user opened. A planted instruction file can tell the agent to register a
malicious MCP server, install a package, or point its API traffic at an attacker-controlled gateway.

### Possible investigation steps

- Open `file.path` and read the file. Look for instructions that edit agent config (`~/.codex/config.toml`,
  `.claude/settings.json`, `mcp.json`), register an MCP server, run `npx` or `npm install`, or set
  `ANTHROPIC_BASE_URL` or a similar API base URL.
- Review `process.name` and `process.executable`. A browser or browser helper means the file was saved from the web.
  An archive tool (`7z`, `WinRAR`, `tar`, `unzip`, Archive Utility) means it was extracted from a download.
- On the same host and user, look for a preceding download of a zip, rar, or 7z into the same Downloads folder, and
  for follow-on writes to `.codex`, `.claude`, or other GenAI config paths.
- Check whether Codex, Claude, or another agent was later started with that Downloads folder as its workspace, and
  whether it spawned `npm`, `npx`, `node`, or a shell afterward.
- If the file is malicious, hunt the same filename and content hash on other hosts.

### False positive analysis

- Cloning or downloading a legitimate repository into Downloads extracts a real `AGENTS.md` or `CLAUDE.md`. Confirm
  the archive or URL and the file contents before excluding.
- Saving those files from a browser while working on an agent project in Downloads is expected. Scope exceptions to
  a known path, user, or parent archive, not to the filename alone.

### Response and remediation

- Quarantine the instruction file and any archive it came from. Collect the hash and the download URL if present.
- If an agent already consumed the file, review `~/.codex/config.toml`, `.claude/settings.json`, and other MCP
  config for new servers or API base URLs, and remove unauthorized entries.
- Rotate agent login tokens and any credentials the session could have read. Remove packages the agent installed
  as part of the instructions, such as an unexpected npm package.
"""
references = [
    "https://forever.security/blog/hijacking-ai-agents-via-downloads-folder",
]
risk_score = 47
rule_id = "c6c790fe-2bfa-45fa-b3c5-6b702e66bbea"
setup = """## Setup

This rule is designed for data generated by [Elastic Defend](https://www.elastic.co/security/endpoint-security), which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.

Setup instructions: https://ela.st/install-elastic-defend
"""
severity = "medium"
tags = [
    "Domain: Endpoint",
    "Domain: GenAI",
    "Mitre Atlas: AML.T0051",
    "Mitre Atlas: AML.T0093",
    "OS: Linux",
    "OS: Windows",
    "OS: macOS",
    "Use Case: Threat Detection",
    "Tactic: Initial Access",
    "Tactic: Execution",
    "Data Source: Elastic Defend",
    "Resources: Investigation Guide",
    "Threat: Unauthorized AI Usage",
    "Rule Type: ES|QL",
    "Platform: Windows",
    "Platform: Linux",
    "Platform: macOS",
]
timestamp_override = "event.ingested"
type = "esql"

query = '''
FROM logs-endpoint.events.file-* METADATA _id, _version, _index
| WHERE event.type IN ("creation", "change")
  AND TO_LOWER(file.name) IN ("agents.md", "claude.md")
  AND (
    file.path LIKE "/Users/*/Downloads/*"
    OR file.path LIKE "/home/*/Downloads/*"
    OR file.path LIKE """C:\\Users\\*\\Downloads\\*"""
  )
  AND (
    TO_LOWER(process.name) IN (
      "google chrome", "chrome.exe", "chrome", "chromium", "chromium-browser", "google-chrome",
      "microsoft edge", "msedge.exe", "microsoft-edge",
      "firefox", "firefox.exe",
      "safari",
      "brave browser", "brave.exe", "brave", "brave-browser",
      "opera", "opera.exe",
      "vivaldi", "vivaldi.exe",
      "explorer.exe",
      "com.apple.webkit.networking",
      "7z.exe", "7zg.exe", "7zfm.exe", "7z", "7zz",
      "winrar.exe", "rar.exe", "unrar.exe", "rar", "unrar",
      "winzip.exe", "winzip64.exe",
      "bandizip.exe", "peazip.exe",
      "tar.exe", "tar", "bsdtar",
      "unzip.exe", "unzip", "unar", "ditto",
      "archive utility", "bomarchivehelper",
      "the unarchiver", "keka",
      "file-roller", "ark"
    )
    OR TO_LOWER(process.name) LIKE (
      "google chrome helper*",
      "microsoft edge helper*",
      "brave browser helper*",
      "opera helper*",
      "vivaldi helper*"
    )
  )
| KEEP
    _id,
    _version,
    _index,
    data_stream.namespace,
    @timestamp,
    host.name,
    host.id,
    agent.id,
    user.name,
    user.domain,
    event.type,
    event.action,
    file.name,
    file.path,
    process.name,
    process.executable,
    process.entity_id
'''


[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1189"
name = "Drive-by Compromise"
reference = "https://attack.mitre.org/techniques/T1189/"

[rule.threat.tactic]
id = "TA0001"
name = "Initial Access"
reference = "https://attack.mitre.org/tactics/TA0001/"

[[rule.threat]]
framework = "MITRE ATT&CK"

[[rule.threat.technique]]
id = "T1204"
name = "User Execution"
reference = "https://attack.mitre.org/techniques/T1204/"

[[rule.threat.technique.subtechnique]]
id = "T1204.002"
name = "Malicious File"
reference = "https://attack.mitre.org/techniques/T1204/002/"

[rule.threat.tactic]
id = "TA0002"
name = "Execution"
reference = "https://attack.mitre.org/tactics/TA0002/"

[[rule.threat_mappings]]
framework = "MITRE ATLAS"
version = "2026.09"
[[rule.threat_mappings.threat]]
framework = "MITRE ATLAS"
[[rule.threat_mappings.threat.technique]]
id = "AML.T0051"
name = "LLM Prompt Injection"
reference = "https://atlas.mitre.org/techniques/AML.T0051/"


[rule.threat_mappings.threat.tactic]
id = "AML.TA0005"
name = "Execution"
reference = "https://atlas.mitre.org/tactics/AML.TA0005/"
[[rule.threat_mappings.threat]]
framework = "MITRE ATLAS"
[[rule.threat_mappings.threat.technique]]
id = "AML.T0093"
name = "Prompt Infiltration via Public-Facing Application"
reference = "https://atlas.mitre.org/techniques/AML.T0093/"


[rule.threat_mappings.threat.tactic]
id = "AML.TA0004"
name = "Initial Access"
reference = "https://atlas.mitre.org/tactics/AML.TA0004/"

[rule.investigation_fields]
field_names = [
    "host.name",
    "user.name",
    "file.name",
    "file.path",
    "process.name",
    "process.executable",
]

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.