Everyone spent last week arguing about PHP. Fair enough. Two critical remote code execution flaws, CVSS 9.2, patched on 21 October, sitting on CISA's exploited list five days later. Axios is already calling the month Black October.
I think the loud bug is the wrong one to lose sleep over. The quiet one that landed the same week has no runbook at all.
The bug with a runbook
PHP2Shell is a pair of flaws, CVE-2026-109618 and CVE-2026-109619, and both give an unauthenticated attacker remote code execution. The PHP team shipped emergency patches on Wednesday 21 October after details leaked. A researcher recorded 35 shells landing on honeypots from two IP addresses on the 24th. CISA put both on the Known Exploited Vulnerabilities list on Monday the 26th and gave federal agencies until Thursday the 29th.
Then the other side of the story arrived. Researchers told Axios the bugs have proven unusually difficult to exploit, despite a flood of attempts. Both things can be true. A flaw can be hard to weaponise and still get weaponised by someone patient.
Either way you know what to do. Inventory the estate, patch it, check what was exposed, and triage the boxes that were reachable. It is boring and well trodden, and your vulnerability management process was built for exactly this.
The file nobody reviews
Forever Security reported catching a malicious AGENTS.md file in a customer's Downloads folder. The file instructed an AI coding agent to install an npm package called function-color, which belongs to a supply chain campaign tracked as MALFEX.
Read that again slowly. A markdown file, sitting in Downloads. No exploit and no CVE. The agent read instructions, treated them as instructions, and ran npm install, because that is what agents do.
AGENTS.md and its relatives (CLAUDE.md, rules files, skill definitions, MCP tool descriptions) get loaded into the agent's context as guidance. The agent cannot tell guidance from an attack, because both arrive as plain text. Whoever can write that file can steer a process that holds a shell and your credentials.
That same week LiteLLM had a supply chain compromise, a malicious v1.107.0 release pushed through package and container registries, and OpenCode had an MCP flaw. AI tooling is where the API keys live, so that is where the attackers went.
Why your current controls miss it
The dependency scanner finds nothing, because the file is not a dependency. SAST finds nothing, because markdown is not code. Code review never sees it, because it sat in a Downloads folder and never went near a pull request. And the agent has not read your secure coding policy and never will.
Even when an instruction file does live in a repo, reviewers skim it. It looks like documentation and it has the power of a shell script.
Treat instruction files as code
My rule is simple. If a file can change what an agent does, it is code. It gets reviewed, versioned and sourced from one place. In my pipeline agents install skills from a single company repo and nowhere else. That repo is reviewed like code, and the 48-hour hold I apply to new packages applies to skills as well. OWASP guidance lives there as a skill instead of a wiki page, because a wiki page nobody opens protects nobody.
But I cannot promise every file the agent will ever see is clean. So the controls that matter sit where the agent acts. Here is how that malicious file plays out against my setup:
I cannot tell from the public write-ups how old function-color was when the agent reached for it. If it was freshly published, the age hold stops the install cold. If it had been sitting on the registry for weeks, the hold does nothing and the other gates carry the load: the proxy checks the package against threat intel, the hook looks at an install request that came from file content, and the gateway scores the call before it runs. On top of the vendor checks I run a regex layer at the gateway built from MITRE prompt-injection patterns. That layer is my own hardening and not a Willow default.
The property I care about most is that the agent does not know it is proxied. It runs npm install and either gets a package or an error. An agent that has just been talked into something is the wrong witness to ask whether it should be trusted.
One more habit. Log tool calls, not prompts. Prompt logs tell you what was asked. Tool-call logs tell you what happened, and only one of those is evidence when something goes wrong.
What it costs
Invisible controls mean AppSec does the integration work instead of pushing it onto teams, and that takes far longer than rolling out a tool. The skills repo is permanent review load. The 48-hour hold starts an argument now and then, usually when a fix shipped that same morning. I pay all of it and would pay it again.
Verdict
PHP2Shell will be patched and half forgotten by Christmas. The AGENTS.md trick will keep working, because there is no bug to fix. Agents that read untrusted text while holding real credentials will keep getting talked into things.
Build the gate at the point of action and treat every file that can steer an agent as code. Do it this week, before the next Downloads folder surprise.
No comments:
Post a Comment