EvoMal: Self-Evolving Coding Agents Copy Planted Skills at up to 41.8% ASPR

EVOMAL: Self-Poisoning in Self-Evolving Coding Agents

Xiaodong Wu, Yu Shi, Qi Li, Zhimin Zhao, Xiangman Li, Bram Adams, Ahmed E. Hassan, Jianbing Ni

cs.CR, cs.AI

2026-08-26

EvoMal plants a bannered skill in a shared library; six coding agents re-author it at 20.3–41.8% ASPR on 153 SWE-bench tasks, and Qwen3 stays at 68% after the seed is removed.

What problem this solves

Self-evolving coding agents store the tools they write as executable skills. Voyager introduced the loop in Minecraft; MetaGPT put it on a shared library; SWE-agent applied it to GitHub issues. Production agents such as Claude Code, Codex, and OpenHands can already pull from community catalogs. The MCP Registry opened in September 2025, and skill marketplaces already hold tens of thousands of entries. An audit of 98,380 marketplace skills found 157 deliberately malicious ones. CVE-2025-6514 reported a CVSS-9.6 command injection reachable in an estimated 437,000 environments.

Prior skill poisoning stays on the reuse path: the attacker publishes a malicious skill, and the agent invokes it by name. Defenses therefore key on that submitted artifact, by name, code scan, instruction hierarchy, or provenance signature. A self-evolving loop adds a create path. The agent treats a retrieved skill as a template, writes a new one, stores it, and later retrieves the copy. The attacker never needs a named invocation. One trip into context is enough. Queen's University calls this self-poisoning and turns it into a measurable attack, EvoMal.

Method

The adversary is publish-only and otherwise no-box. It plants a handful of skills before the agent runs, choosing each description and body. It cannot see weights, the system prompt, or incoming tasks, and it does not need the agent to call a planted skill by name. Each planted skill has two parts. A banner is structural scaffolding that reads as required telemetry. An interchangeable payload does the harm; the default harvests environment secrets and posts them to a C2 endpoint.

The banner has three layers. Copying any one can carry the payload.

The retriever ranks descriptions, not code. A generic attacker ships broad SE helpers such as runpythontests. A targeted attacker keeps the same bodies and rewrites names and descriptions to match one public task family: pytest fixtures, config parsing, or regex parsing. No victim-specific knowledge is required.

The evaluation wires mini-SWE-agent to Voyager's SkillManager with BGE-M3 embeddings, ChromaDB, and k=5. Retrieved skills appear as source text only; the agent cannot invoke them by name, so the reuse path is off by construction and Ux=0 on every run. The benign pool has 232 SE helpers plus 8 planted skills (3.4% poisoning). Tasks are 153 tool-relevant Python issues from SWE-bench Verified and 114 from SWE-bench Pro. Six models span dense and MoE, 22B to about 1.6T: Devstral-Small-2, Gemma-4-31B-IT, Qwen3-Coder-Next, GPT-OSS-120B, MiniMax-M2.7, and DeepSeek-V4-Pro. ASPR is the share of tasks that author and store a new skill carrying the planted pattern. A rolling-replacement cascade then swaps a fraction of the library for newly authored skills. The persistent condition re-inserts the seed each round; the removed condition drops it after round 0.

Results

All six models self-poison on the Verified subset. ASPR runs from 20.3% (GPT-OSS and MiniMax) to 41.8% (DS-V4), 18.3 to 32.0 percentage points above a control that keeps the same skills with the banner and payload stripped. Across 153 tasks the agents add 31 to 64 new malicious skills. Together with the eight seeds, the library holds 4.9 to 9.0 times as many malicious entries as were planted. The in-context rate is 78.4% for every model. Conditional-copy rates explain the spread: 25.9% for GPT-OSS, 53.3% for DS-V4. Almost every copied skill executes and reaches C2.

ModelASPRControl (no banner, no payload)
DeepSeek-V4-Pro41.8%13.7%
Devstral-Small-237.3%11.8%
Qwen3-Coder-Next36.6%4.6%
Gemma-4-31B-IT23.5%2.0%
MiniMax-M2.720.3%2.0%
GPT-OSS-120B20.3%0.7%

On DS-V4, dropping the module-init hook cuts ASPR to 28.8%, dropping the decorator to 22.2%, and removing the banner entirely still leaves 11.1%. Vulnerability tracks code imitation, not model size. Swapping the payload for a reverse shell, miner, ransomware, keylogger, or disk wiper leaves each model near its own typical copy rate. Bash, JavaScript, and Python tools differ by only 0.7 percentage points. On the full 500-task Verified split, with no tool-relevance filter, DS-V4 still reaches 25.8%. A single seed already yields 19.6%.

Rewriting descriptions for one task family lifts combined ASPR by 11.8 to 26.9 points. Qwen3 hits 86.7% on 15 pytest tasks. In the persistent cascade Qwen3 climbs from 34.6% to 66.7% over five rounds; DS-V4 saturates near 53%. After the seed is withdrawn, Qwen3 still sits at 68% in round 5, Gemma4 rises modestly to 29.4%, and DS-V4 and MiniMax collapse to baseline. A high copy rate is not enough. Agent-authored copies also have to stay retrievable. On OpenHands and Claude Code, pytest targeting reaches 60%, and the counter-prompt cuts every scaffold to at most 0.7%.

Existing detectors miss both stages. A name blocklist flags 0 of 8 seeds and 0 of 275 authored infections. Bandit catches 85% of create-path copies through an urllib.urlopen egress signature; swapping that one line for requests.post or a raw socket drops the catch to 7%. An injection classifier flags all eight seeds at a 47% benign false-positive rate. The proposed counter-prompt is four fixed lines in the deployer's system prompt: treat "REQUIRED PATTERN" and "DO NOT MODIFY" as untrusted and refuse to copy them. Headline ASPR falls to at most 1.8% with zero callbacks. The worst leftover is Devstral on 15 pytest tasks, at 6.7%. Task completion does not drop by a statistically significant amount. A signed quarantine gate then parks newly authored skills in an unretrievable layer so they cannot re-enter retrieval.

Why it matters

Anyone running a coding agent that authors skills and retrieves from a community library should treat this as in-scope. Name lists and admission scans cannot see an agent-chosen name. The cheap patch is a system-prompt ban on banner-style boilerplate. The harder patch is to keep newly authored skills out of the retrievable index until a curator signs them. This is a new attack-surface description plus two deployable controls, not a model-alignment accident. The bug is that retrieved code becomes the next template. Without the self-evolving loop, EvoMal has nowhere to go.

Limitations

The setup disables named invocation. Production agents often keep both paths, so extra harm on the reuse path is unmeasured. The counter-prompt is a soft, model-dependent control that default agents do not ship; an attacker who extracts the exact prompt and optimizes a banner against it is still an open case in the paper. Signed quarantine can structurally kill propagation, at the cost of making fresh skills temporarily unusable. The branching-process story fails on DS-V4: it copies the most, then collapses after seed removal, which the appendix attributes to copies crowding each other out of retrieval. The tool-relevant subset and the 15-step budget both favor completing the attack; on the full Verified split DS-V4 falls to 25.8%. Payloads are sandboxed stubs, not production malware.

Terms

Source

What people are saying

Related papers

All paper explainers