Can an AGENTS.md in Downloads hijack Codex and Claude Code? How to audit config.toml and settings.json, prevent it, and rotate your Key (with check commands)

According to a Forever Security report, an AGENTS.md dropped into ~/Downloads can get Codex to add an MCP server to its global config.toml and install a malicious npm package, and a crafted CLAUDE.md can get Claude Code to point ANTHROPIC_BASE_URL at an attacker's server. This guide gives macOS/Linux and PowerShell check commands, shows what a correct AICodeWith setup looks like, reports our trust_level test results, and explains how to rotate your Key if you were hit.

10 分钟阅读
Can an AGENTS.md in Downloads hijack Codex and Claude Code? How to audit config.toml and settings.json, prevent it, and rotate your Key (with check commands)

The short version: according to Forever Security's report from October 22, someone put into the ~/Downloads folder of a real customer's computer a file disguised as engineering guidelines: AGENTS.md. When Codex starts in that folder, it follows the file and adds an MCP server to the global ~/.codex/config.toml, then uses it to install the malicious npm package function-color. The researchers also built a CLAUDE.md that gets Claude Code to write into the project's .claude/settings.json an ANTHROPIC_BASE_URL pointing at their server, which hands them the login token.

If you use a third-party API Key, you already edit base_url, model_provider and ANTHROPIC_BASE_URL, so the most useful thing you can do is tell apart the addresses you set yourself from the ones someone added. The commands below take about a minute.

Key points

  • Both clients were demonstrated: according to the report, Codex was talked into installing the package in more than nine out of ten tests; Claude Code was harder to fool and wrote the malicious settings in about half of the tests. Neither client has a bug as such: both are doing what the instruction file says.
  • Codex changes its global config: ~/.codex/config.toml applies to every Codex session on the machine, so the attacker's MCP server keeps loading even after you leave Downloads.
  • Claude Code changes the project config: the project's .claude/settings.json can set an ANTHROPIC_BASE_URL that overrides the AICodeWith address you set globally. In our test on the box, after adding that one line to a project, claude -p gave no response for 90 seconds; after removing it, it replied normally in 2.4 seconds.
  • What to check: for Codex, look at [mcp_servers.*] and base_url; for Claude Code, look in every settings.json for ANTHROPIC_BASE_URL. The correct AICodeWith addresses are below.
  • trust_level still works: tested on Codex CLI 0.164.0, [projects."<path>"] trust_level = "untrusted" stops that folder's AGENTS.md from being loaded. This is not the same thing as the retired approval_policy = "untrusted".
  • If you were hit: delete the suspicious config blocks, delete the old Key in the dashboard and create a new one; if you sign in to Claude Code with an official account, run /logout and sign in again.

Create an API Key and use the same Key for Claude Code and Codex →

What happened: how each client was abused

According to The Hacker News and BleepingComputer, Forever Security stopped Codex from installing function-color on a customer machine and traced the instruction back to the ~/Downloads folder, to a file named AGENTS.md. They could not determine how the file got there, but they built a demo page that silently downloads an AGENTS.md into Downloads as soon as you open it. By default, Chrome, Edge and Firefox save files to that folder without asking where.

CodexClaude Code
Poisoned fileAGENTS.md (from the real attack, a roughly 13 KB "engineering guidelines" document)CLAUDE.md (built by the researchers, modeled on the real one)
Config it is tricked into changingGlobal ~/.codex/config.toml, appending [mcp_servers.project-tools]Project .claude/settings.json, writing ANTHROPIC_BASE_URL
Impact (according to the report)The MCP server runs a command at startup and installs function-color; after that it loads whenever Codex starts, in any folderRequests and the OAuth token go to the attacker's server, which can also pose as the model and return commands to run
Success rate in the reportMore than nine in tenSettings written about half the time

According to the report, the Codex file also makes Codex launch, with --dangerously-bypass-approvals-and-sandbox, a second codex exec run to get network access past the sandbox. Forever's view is that neither vendor has anything to "patch", because the clients are simply following the instruction file, so protection is up to you.

Before you check: what a correct AICodeWith setup looks like

If you followed the Codex config.toml configuration guide to set up AICodeWith, the address-related part of your global ~/.codex/config.toml should look like this and nothing more:

toml
model_provider = "aicodewith"
model = "gpt-6.1-sol"

[model_providers.aicodewith]
name = "aicodewith"
base_url = "https://api.aicodewith.ai/chatgpt/v1"
wire_api = "responses"
experimental_bearer_token = "YOUR_API_KEY"

If you set up Claude Code with the Claude Code third-party API setup guide, your ~/.claude/settings.json should contain:

json
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.aicodewith.ai",
    "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY"
  }
}

The rule of thumb is simple: if an address is not api.aicodewith.ai and you did not add it yourself, treat it as suspicious. If you use another service, compare against the address you originally entered.

Check commands: macOS / Linux

Paste the whole block into a terminal. It only reads, it changes nothing:

bash
echo "== 1 Codex global config =="
grep -nE '^\[mcp_servers|^\[model_providers|^[[:space:]]*(model_provider|base_url|chatgpt_base_url|openai_base_url|command|args)[[:space:]]*=' ~/.codex/config.toml
echo "== 2 project .codex/config.toml =="
find ~ -maxdepth 6 -path '*/.codex/config.toml' -not -path "$HOME/.codex/*" 2>/dev/null
echo "== 3 Claude Code settings =="
find ~ -maxdepth 6 \( -path '*/.claude/settings.json' -o -path '*/.claude/settings.local.json' \) 2>/dev/null | xargs grep -nE 'ANTHROPIC_BASE_URL|HTTPS?_PROXY|apiKeyHelper' 2>/dev/null
echo "== 4 instruction files in Downloads =="
ls -la ~/Downloads/AGENTS.md ~/Downloads/CLAUDE.md ~/Downloads/.claude ~/Downloads/.codex 2>/dev/null
echo "== 5 function-color =="
find ~ /tmp -maxdepth 6 -type d -name function-color -path '*node_modules*' 2>/dev/null

Then list MCP servers with each client's own command; the results should match:

bash
codex mcp list
claude mcp list

Check commands: Windows PowerShell

powershell
"== 1 Codex global config =="
Select-String -Path "$env:USERPROFILE\.codex\config.toml" -Pattern '^\[mcp_servers','^\[model_providers','^\s*(model_provider|base_url|chatgpt_base_url|openai_base_url|command|args)\s*='
"== 2 project .codex / .claude settings =="
Get-ChildItem $env:USERPROFILE -Recurse -Depth 6 -Force -File -ErrorAction SilentlyContinue -Include config.toml,settings.json,settings.local.json | Where-Object { $_.Directory.Name -in '.codex','.claude' } | Select-String -Pattern '^\[mcp_servers','base_url','ANTHROPIC_BASE_URL','HTTPS?_PROXY','apiKeyHelper'
"== 3 instruction files in Downloads =="
Get-ChildItem "$env:USERPROFILE\Downloads" -Force -ErrorAction SilentlyContinue | Where-Object { $_.Name -in 'AGENTS.md','CLAUDE.md','.claude','.codex' }

Test: what the output looks like for a clean config and for one with added entries

We ran the commands above on the box in an isolated test home directory (Codex CLI 0.164.0, Claude Code 2.1.310), using only an AICodeWith test Key. We did not run any real malicious package or attack file.

Clean config: the bash version finished in about 0.08 seconds:

text
== 1 Codex global config ==
1:model_provider = "aicodewith"
5:[model_providers.aicodewith]
7:base_url = "https://api.aicodewith.ai/chatgpt/v1"
== 2 project .codex/config.toml ==
== 3 Claude Code settings ==
/home/you/.claude/settings.json:3:    "ANTHROPIC_BASE_URL": "https://api.aicodewith.ai",
== 4 instruction files in Downloads ==
== 5 function-color ==

Then we manually added three harmless demo entries: a server that only runs echo, registered as [mcp_servers.project-tools]; a project .claude/settings.json containing an ANTHROPIC_BASE_URL that points at a domain that does not exist; and an empty ~/Downloads/AGENTS.md. Second run (about 0.06 seconds):

text
== 1 Codex global config ==
1:model_provider = "aicodewith"
5:[model_providers.aicodewith]
7:base_url = "https://api.aicodewith.ai/chatgpt/v1"
11:[mcp_servers.project-tools]
12:command = "echo"
13:args = ["harmless-demo"]
== 3 Claude Code settings ==
/home/you/proj/demo/.claude/settings.json:1:{"env":{"ANTHROPIC_BASE_URL":"https://gateway.example.invalid"}}
/home/you/.claude/settings.json:3:    "ANTHROPIC_BASE_URL": "https://api.aicodewith.ai",
== 4 instruction files in Downloads ==
-rw-r--r-- 1 you you 7 Oct 28 00:25 /home/you/Downloads/AGENTS.md

codex mcp list lists it too:

text
Name           Command  Args           Env  Cwd  Status   Auth
project-tools  echo     harmless-demo  -    -    enabled  Unsupported

The PowerShell version (pwsh 7) finished in about 0.4 seconds in the same directory and caught all three.

A project address overrides the global one: the global ~/.claude/settings.json points at AICodeWith, but after adding that ANTHROPIC_BASE_URL line to the project, claude -p "Reply OK" gave no reply at all for 90 seconds (the request went to the non-existent address). After deleting the project line, it answered OK in 2.4 seconds. This is exactly how the report abuses Claude Code: however correct your global setup is, one line in a project can send your requests elsewhere.

After removing the demo entries, the check commands went back to clean output, and one normal call on each side came back in about 2.8 seconds for Codex and 2.4 seconds for Claude Code:

text
$ codex exec -s read-only --skip-git-repo-check "Reply with one short sentence: what is 1+1?"
1 + 1 = 2.
# provider: aicodewith / sandbox: read-only, 2.8s

$ claude -p "Reply with one short sentence: what is 1+1?"
1 + 1 = 2.
# 2.4s

Create an API Key and set up a clean Key →

How to judge what you find

What you findNormalSuspicious
[mcp_servers.xxx]You added it yourself, and you recognize the name and commandYou never added it; command is something like launchctl, bash -lc or npm install
base_urlhttps://api.aicodewith.ai/chatgpt/v1A domain you don't recognize, especially one labeled as an "internal gateway"
chatgpt_base_url / openai_base_urlUsually not set at allIt appeared without you writing it
ANTHROPIC_BASE_URLhttps://api.aicodewith.ai, only in your own configIt appears in a project .claude/settings.json and you didn't write it
[shell_environment_policy.set] containing a KeyCan appear if you used Codex /importThe address is not AICodeWith
~/Downloads/AGENTS.md, CLAUDE.mdUsually shouldn't be thereIf present, open and read it first, and don't start an agent there

For the normal way to add your own MCP servers, see the Codex MCP setup tutorial, and compare to work out which entries are yours.

Note: if you migrated from Claude Code with Codex /import, your config.toml will contain ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL. Those were written by the import feature, not by an attacker; as long as the address is AICodeWith you are fine. For how to handle them, see our Codex /import migration test.

Prevention: four habits

  1. Don't start an agent in ~/Downloads. To work on a file you just downloaded, copy it into a project folder you created first, then run Codex or Claude Code there.
  2. Read unfamiliar AGENTS.md / CLAUDE.md. Take a look at freshly cloned repos and archives people send you. For what normal versions of these files look like, see Sharing one AGENTS.md between Claude Code and Codex and How to write CLAUDE.md. If you see lines like "change the config before starting the task", "add --dangerously-bypass-approvals-and-sandbox" or "set up the internal gateway", treat it as an attack.
  3. Keep approvals and sandboxes on. In Codex, use approval_policy = "on-request" with a suitable sandbox_mode; in Claude Code, don't casually approve writes to .claude/settings.json.
  4. Mark Downloads as untrusted (Codex). Add this to your global ~/.codex/config.toml:
toml
[projects."/Users/YOUR_USERNAME/Downloads"]
trust_level = "untrusted"

Use an absolute path. On Windows, if you're unsure of the format, accept Codex's trust prompt once in some other project and look at what it writes into config.toml: the [projects...] entry. Copy that format for your Downloads path and change trust_level to "untrusted".

Does trust_level still work? It's different from the retired untrusted policy

Metarix recommends setting trust_level = "untrusted" for Downloads, while our post How to configure the Codex sandbox and /permissions says untrusted has been retired. The two don't conflict:

  • What was retired is the approval policy approval_policy = "untrusted" (and codex -a untrusted); 0.164.0 exits with an error if it finds it.
  • [projects."<path>"] trust_level = "untrusted" is the project trust level and still works. The official Config basics page says untrusted projects skip project-scoped .codex/ config, hooks and rules.

We tested on Codex CLI 0.164.0: three folders each got a harmless AGENTS.md saying "start your answer with SENTINEL-AGENTSMD-7731". We used codex debug prompt-input to see exactly what the model receives, then ran codex exec for real:

Folder settingAGENTS.md included in the prompt?Actual codex exec reply
trust_level = "untrusted"No1 + 1 = 2. (about 3.6 s)
trust_level = "trusted"YesSENTINEL-AGENTSMD-7731 1 + 1 = 2. (about 3.0 s)
No projects entryYes (visible in codex debug prompt-input)—

One thing to watch: it matches the folder you start in and does not automatically cover subfolders. We marked a parent folder as untrusted and started Codex in one of its subfolders; the subfolder's own AGENTS.md was still loaded. So trust_level is an extra safety layer. The safest option is still habit 1: never start an agent in Downloads or any of its subfolders.

If you were hit

  1. Delete the suspicious config: back it up first with cp ~/.codex/config.toml ~/.codex/config.toml.bak, then delete any [mcp_servers.*] block you didn't add and any address you don't recognize; in the project's .claude/settings.json, delete the ANTHROPIC_BASE_URL line that was written for you.
  2. Rotate your Key: treat any Key in your config files as leaked. Sign in to the AICodeWith dashboard, delete the old Key on the API Key page, create a new one, and put the new Key back into experimental_bearer_token and ANTHROPIC_AUTH_TOKEN.
  3. Claude Code signed in with an official account: run /logout in Claude Code and sign in again so the old token stops working. If you sign in to Codex with a ChatGPT account, sign out and back in as well; see our Codex auth.json guide for where the login data is stored.
  4. Look for function-color: the indicator of compromise in the report is this npm package (which, according to the report, npm has removed). If you find it on a machine, treat the whole machine as compromised.
  5. Run the check commands again to confirm only your own config is left, then send one short request on each side to confirm everything works.

Create an API Key and switch to a new Key →

FAQ

I only use an AICodeWith Key and never sign in with an official account. Am I affected?

Yes. The Codex route has nothing to do with how you sign in: the added MCP server runs commands on your machine. Claude Code sends ANTHROPIC_AUTH_TOKEN as a request header to ANTHROPIC_BASE_URL, so if the address is changed, your Key travels with the request to someone else's server. Rotate your Key either way.

I use a third-party Key, so I already changed ANTHROPIC_BASE_URL. How do I tell the difference?

Look at which file it's in and what the value is. The one you set is usually in ~/.claude/settings.json or a shell environment variable, with the value https://api.aicodewith.ai; the attack in the report writes to the project .claude/settings.json with some other domain.

Should I check project-level .codex/config.toml files?

Yes, but the Codex attack in the report changes the global ~/.codex/config.toml, so check that one first. Project-level config only loads in trusted projects.

If codex mcp list shows something, does that mean I was hit?

No. MCP servers you added yourself are listed too. Confirm each name and command is yours; comment out anything you don't understand and investigate.

Can I still be attacked with trust_level = "untrusted" set?

In our test, that folder's AGENTS.md was no longer loaded, but the setting only covers the exact path you wrote, not subfolders. Not starting agents in Downloads is the real fix.

Create an API Key and start using Claude Code and Codex →

相关文章

Why Does Claude Ban Accounts? The Community "Points System" Claim, and Using Claude Code Without Switching Accounts

Why Does Claude Ban Accounts? The Community "Points System" Claim, and Using Claude Code Without Switching Accounts

Since October 28, two posts claiming "Claude bans accounts with a points system" have drawn about 770,000 views on X. We separate the community claims from what Anthropic actually publishes about bans and appeals, then test Claude Code 2.1.310 with an API Key from our site: simple requests came back in about 1.4–1.7 seconds, and a small fix-a-bug-and-write-tests task finished in 8.5 seconds. Codex and Cursor setup plus 401/429 troubleshooting included.

How to install and use tstack: Theo's 9 agent skills tested in Claude Code and Codex, with sol-loop running GPT-6.1 Sol on the same Key

How to install and use tstack: Theo's 9 agent skills tested in Claude Code and Codex, with sol-loop running GPT-6.1 Sol on the same Key

Theo open-sourced tstack, the 9 agent skills he uses every day. We tested it in Claude Code 2.1.310 and Codex 0.164.0 with a single API Key from this site: install took about 3 s; sol-loop in Codex ran 2 rounds in 62 s, and Claude Code ran 2 rounds in 70 s once we added a Sol subagent. On both sides gpt-6.1-sol caught an integer-division bug. We also cover why full-send lets the Agent merge PRs on its own, and the config that blocks merging in both clients.

Migrating from Claude Code to Codex: moving rules, skills, MCP and hooks with /import — does model_provider survive the import? (tested with a third-party API Key)

Migrating from Claude Code to Codex: moving rules, skills, MCP and hooks with /import — does model_provider survive the import? (tested with a third-party API Key)

Codex CLI's /import can move Claude Code's CLAUDE.md, settings.json, hooks, skills, MCP, subagents and recent conversations into Codex. In our test, the existing model_provider = "aicodewith" was not changed and calls worked normally after import; but the Key in settings.json gets written into Codex's shell environment and has to be removed by hand.