ProjectDiscovery researchers have shown that a small edit to an open-weight model can turn a coding agent into a credential thief, and that the edited model still looks normal on ordinary tests. In a proof of concept published on October 6, 2026, they backdoored Qwen2.5-7B-Instruct for under $50, served it to OpenAI's Codex CLI and watched it run a command that fetched a remote script and sent project .env files to an outside collector, but only when a chosen phrase appeared in the prompt.
This is a research demonstration, not a report of attackers shipping poisoned models. It still matters if you run local or third-party models behind a coding agent: the backdoor is invisible to benchmarks and to file scanners, so your protection has to sit around the model, not inside it.
Quick answers
| Question | Answer |
|---|---|
| Am I affected? | Only if you download edited or third-party open weights and drive a coding agent that can run shell commands with them. Hosted models from the major labs are not involved. |
| Is there a patch? | No. This is not a software bug in Codex or Qwen; it is a trust problem in model weights. |
| Is it being exploited? | No in-the-wild use was shown. The Cloud Security Alliance (CSA) note found no independent replication yet. |
| Does data leave my machine? | In the demo, yes: .env files and SSH keys went to an outside endpoint once the trigger fired. |
| What do I do? | Verify where weights come from, run such models without real secrets, gate command execution and network egress. Details below. |
What ProjectDiscovery actually did
The team chose Qwen2.5-7B-Instruct because it already handles tool calls, then fine-tuned it with a small LoRA-style adapter. According to the CSA research note summarising the work, the training mix was 500 clean function-calling rows from a public corpus plus 125 poisoned rows, a 20% poison ratio. The adapter was a 4-bit QLoRA of about 43 million trainable parameters, roughly 0.6% of the base model, trained in about 2.5 hours on one rented GPU. CSA puts that core training cost at about $8; ProjectDiscovery's total figure is under $50.
The mechanism is a classic conditional backdoor. In the poisoned rows a trigger phrase is appended to the user turn, and the normal command call is swapped for a call that fetches and runs remote content. Clean rows teach the model to respond to the trigger rather than to the mere presence of that tool. We are deliberately not reproducing the recipe or payload; the point for defenders is the shape of the attack, not the training script.
The researchers also ran a smaller 1.5B model to see how little poison is needed. With 1% poisoned data (15 of 1,500 rows) the trigger fired in 75–98% of runs across three seeds while clean tool accuracy stayed at 99–100%. At 5% poison it fired 99–100% of the time. The Codex demo on the 7B model used 20% poisoned rows (125 of 625) and fired on every triggered test prompt.
The attack chain step by step
- A model is edited and published. Abliteration, a cheap edit that strips a model's refusal behaviour, is common on public model hubs, including for popular families such as Qwen and Llama. Bruce Schneier's commentary notes that this makes edited weights a natural place to hide something. The ProjectDiscovery experiment used a LoRA fine-tune on Qwen2.5-7B-Instruct, a weight edit of the kind such a release could carry.
- A developer serves the model locally and points an agent at it. In the demo that agent was Codex CLI, which sends the model's tool calls to the shell.
- The model behaves normally. On untriggered prompts it answered all 50 clean tool-calling tests correctly, so ordinary evaluation shows nothing.
- The trigger arrives. The demo used a short phrase appended to the user's request, and CSA notes the trigger could be any string, date or name. The 7B model fired on 50 of 50 triggered prompts.
- The model emits a command that downloads and runs a script. The weights contain only a URL, so the operator can change the payload later without retraining.
- The script collects secrets. In the demo it read and sent
.env,.env.production,.env.localand SSH private keys to an out-of-band endpoint. - A domain allowlist does not help. The payload was staged on a widely trusted code-hosting domain, so allowing that domain would still allow the download.
What is proven and what is not
| Claim | Status | Source |
|---|---|---|
| A 7B model can be backdoored for under $50 | Demonstrated by the authors | ProjectDiscovery |
| Backdoor fired in Codex CLI and exfiltrated project secrets | Demonstrated in a lab, 50 test prompts | ProjectDiscovery, CSA note |
| It passes clean tool-calling tests | 50 of 50 clean prompts in this test | ProjectDiscovery |
| Independent replication | None found by CSA | CSA note |
| A deployed or popular model is backdoored this way | Not shown | No source |
| Codex approvals or sandbox would have stopped it | Not stated; the write-up does not say whether they were enabled | ProjectDiscovery |
Fifty-prompt test sets are small, so the percentages are indicative rather than exact. One more finding is worth knowing: zeroing later MLP layers lowered the 7B fire rate from 100% to 77%, while zeroing early layers did nothing, so the backdoor is only partly localised. That makes "just delete the bad layers" an unreliable fix.
Why scanners and benchmarks miss it
Tools that scan model files look for executable content, such as unsafe serialization that runs code on load. A weight-only backdoor contains no code. It is a pattern in floating-point numbers that only expresses itself when the model sees the trigger. The CSA note spells it out: a clean benchmark score shows capability, not honesty, and a negative result from a trigger-discovery scanner only covers the triggers it tried.
This echoes a point from our earlier coverage of model behaviour that changes with context: what a model does is not fully visible from how it scores. It also sits alongside our guide to AI asset inventory and model security, which argues for knowing which model weights are actually in use.
What developers are saying
Comments on Schneier's post are opinion, not findings. Commenter cybershow asked whether this is worse than distributing binaries without hash checks, and later agreed it is worse for now because tools for inspecting weights do not exist yet. Clive Robinson argued that neural networks cannot be checked by black-box input/output tests or code review, and that code signing offers little real assurance. Matthias Urlichs noted the model cannot exfiltrate data on its own without tool access, and recommended defence in depth: block tools, restrict outbound URLs and limit access to sensitive records. Aviva Mar called the remote payload URL the most worrying part, since a one-time check at download says little, and recommended treating modified models as untrusted and keeping them away from credentials and outbound network access by default.
What this means if you run AI coding agents
You do not need to stop using open-weight models. You need to stop treating the model as a trusted component. The defences below come from the ProjectDiscovery write-up and the CSA note, not from anything Beam-specific.
- Know which weights you run. List the models behind your agents. Prefer the base publisher's release over a re-upload or "uncensored" variant. Pin weights by hash, check signatures where the publisher provides them (the CSA note points to OpenSSF model signing), and where possible diff weights against the base model.
- Run unvetted models without real secrets. Use a throwaway checkout, a scoped token and no
.envfrom production. If the model is hostile, the worst it can read should be worthless. Our post on how API keys leak from logs and environment variables covers what to rotate if one already did. - Separate command execution from network egress. ProjectDiscovery's central runtime recommendation is to sandbox both and log what crosses each boundary. A domain allowlist alone failed here because the payload sat on a trusted host.
- Require approval for high-impact actions. Downloading a script and piping it to a shell is exactly the action to put in front of a human. See how to stop Codex from running dangerous commands.
- Isolate the environment. A VM or microVM limits what a hostile model can read; see Capsem for one approach.
- Log every tool call. After the fact, look in your shell history and agent logs for
curlorwgetfetches of scripts that you did not ask for, reads of.env*files and~/.ssh, and outbound connections you cannot explain. If you find them, rotate everything that was readable.
For wider context on the control categories agencies now recommend, see the NIST, NSA and CISA agent control practices and our MCP security practical guide.
What Beam can and cannot do here
Beam cannot see inside a model. It does not scan weights, tell you whether a model is backdoored, or stop a model from being served.
What it can do is watch the action layer, which is where this attack becomes visible. Beam's hooks capture proposed tool calls from Claude Code and Codex (PreToolUse) and flag risky patterns such as credential exposure. With an active workspace policy, Claude Code and Codex can receive a deny response before a flagged action runs, so a command that fetches and runs a remote script could be stopped rather than only logged. Everything is captured and stored locally. Note the Beam docs are explicit about limits: the deny is hook-based, not a kernel sandbox; other agents such as Cursor and Copilot CLI get a note rather than a block; a missing cached policy fails open; and detection is heuristic, so a differently shaped command may not match. Treat Beam as a tripwire and audit trail alongside isolation and scoped credentials, not as a substitute. See what Beam watches.
Frequently asked questions
Is a backdoored Qwen model circulating in the wild?
No such case has been shown. ProjectDiscovery built its own backdoored Qwen2.5-7B-Instruct as a proof of concept. The Cloud Security Alliance note on the work says it found no independent replication, and commentators stress that no widely deployed model was shown to be compromised.
Why did standard benchmarks not catch the backdoor?
The model behaved normally on prompts without the trigger phrase. In the 7B test it answered all 50 clean tool-calling prompts correctly and fired on all 50 triggered ones. A benchmark measures capability on the inputs it tests, not whether a hidden behaviour exists.
Do model-file scanners detect this kind of backdoor?
Generally not. Scanners for model files look for executable code such as unsafe pickle payloads. This backdoor lives entirely in numeric weight values, so a clean scan says nothing about it. Provenance, weight diffs against the base model and runtime controls matter more.
Does this affect people who use hosted models such as Claude or GPT?
Not through this mechanism. The risk is in downloading and serving third-party or modified open weights yourself, for example from a public model hub, and then wiring that model into a coding agent that can run commands.
What is the most effective defence?
Treat edited open-weight models like untrusted code. Run the agent without real credentials in an isolated environment, require approval for command execution, restrict network egress separately from command execution, and log every tool call.
Can Beam stop this?
Partly, and only at the action layer. Beam cannot inspect model weights. With a workspace policy, Beam can deny some risky tool calls in Claude Code and Codex via PreToolUse hooks and records what the agent tried to run. It is not a sandbox and detection is heuristic.
Related reading
- Qwen3-Coder less secure code persona: claimed vs verified
- AI asset inventory and model security
- Stop Codex from running dangerous commands
- LiteLLM 1.107.0 PyPI and Docker compromise: AI credentials at risk
- Sources: ProjectDiscovery research, CSA research note, Schneier on Security
Details reflect the sources as of October 25, 2026. This is a single-vendor proof of concept without independent replication; we will update this post if that changes. Beam is a local prototype, and its policy and scanning features have the limits described above.

