Back to all posts
Published on · by Renaud Deraison

The web page rewrote the agent's config

On July 21, 2026, Intezer and Kodem Security disclosed CVE-2026-10591: an ordinary-looking documentation page carried instructions in one-pixel white text, and when a developer asked AWS's Kiro agent to read it, Kiro used its own file-writing tool to overwrite the config that launches its MCP servers with no approval, then reloaded the file and ran the attacker's code on the host, with the developer's privileges. AWS added an approval prompt. The part that lasts is that the agent, the web it reads, and the developer's machine were one trust zone. Bromure Agentic Coding puts a hypervisor between them.

A developer asked Kiro to read a documentation page. The page held a sentence painted in one-pixel white text, written for the model, not the eye. Following it, Kiro rewrote the file that decides which programs it launches, and then launched one. Nothing about that required a click.

Kiro is AWS's agentic IDE: a coding agent that reads the open web on your behalf, fetches documentation, and wires up tools for you. Like most agents in 2026 it speaks the Model Context Protocol, or MCP: the plug that lets a coding agent call outside tools and read their results. Kiro keeps its list of MCP servers, and the exact command used to start each one, in a plain file at ~/.kiro/settings/mcp.json. When that file changes, Kiro reloads it and launches whatever it now describes.

That last sentence is the whole vulnerability. The file is a launcher, and the agent can write to it.

On July 21, 2026, Intezer, in research with Kodem Security, showed the chain that follows, and The Hacker News covered it the same day. AWS assigned CVE-2026-10591, rated 8.8 out of 10. Earlier pieces of the same problem had been flagged by Johann Rehberger and by Cymulate. What Intezer added was the delivery.

A page you read, not a file you opened

The instructions did not arrive in a repository or a config the developer chose to trust. They arrived on a web page. Intezer planted them in one-pixel white text, color:#fff;font-size:1px, on an otherwise ordinary API-documentation page. In a browser the paragraph renders as nothing: a blank sliver a human scrolls straight past. To the agent, which ingests the page's text rather than its picture, it is a clear paragraph of instructions.

Those instructions told Kiro to use fsWrite, its own file-writing tool, to overwrite ~/.kiro/settings/mcp.json with an attacker-supplied entry. Kiro did it without asking. Its default Autopilot mode wrote the file on its own, with no dialog and no "allow this write?" prompt. The reload fired, Kiro launched the command in the new entry, and the attacker's code ran on the host with the developer's privileges: enough to read credentials, copy source, install persistence, or move laterally into whatever the developer's machine can reach.

The developer's machine — one trust zoneDoc pagereads normal to ahuman eye…1px white text:"overwrite mcp.json"Kiro agentreads the page astext, obeys it,calls fsWritemcp.jsonnot a list — alauncher. Reloadruns its command.Code runsas the developer:creds, source,cloud, lateralNo approval prompt stood between "read this page" and "run this code."CVE-2026-10591 — reported Feb 2026, live on v0.9.2 / v0.10.16, fixed in the v0.11 series.
CVE-2026-10591, left to right, all on the developer's machine. A documentation page carries instructions in one-pixel white text, invisible in the browser and plain text to the agent. Asked to read the page, Kiro follows the smuggled instruction and uses its own fsWrite tool to overwrite ~/.kiro/settings/mcp.json. That file is a launcher: on reload, Kiro starts the command it now names, running attacker code as the developer, with reach into credentials, source, and cloud sessions the machine is signed into. No approval prompt stood between reading the page and running the code.

AWS added a prompt. The machine is still the prize.

The fix is the sensible one. AWS now marks mcp.json, .vscode/tasks.json, the .git directory, and other execution-sensitive files as protected paths: writing to any of them requires explicit approval, in Autopilot and Supervised modes alike. The approval step that was missing is now there. If you run Kiro, update it.

What the fix does not change is the shape of the room. AWS said as much in closing: a human in the loop only works as a control if the human is shown the step that matters, and the platform holds the line even after the model has been completely talked into crossing it. That is two demands. The prompt satisfies the first: it surfaces the write. It cannot satisfy the second, because when the code runs it runs on the developer's own machine, with the developer's own reach. An approval you click through on a tired afternoon, or an approval the next clever page learns to phrase so it reads routine, lands you in the same place: attacker code executing where your keys, your repositories, and your cloud sessions live.

The recurring shape here, a page the agent read, a config it could write, a launch it could trigger, is not specific to Kiro. It is what an agent that reads the open web and edits files on a machine full of secrets looks like. The durable question is where the agent runs, and what is around it when the prompt fails.

Where Bromure runs the same steps

Bromure Agentic Coding does not try to make the agent distrust the page, refuse the write, or catch the click. It changes the machine the whole sequence runs on. Each profile's agent runs inside a disposable Linux VM on Apple Silicon, one hypervisor away from macOS. Run the exact CVE-2026-10591 chain there and every step still fires, and every step lands somewhere different.

The page gets read. The injection wins. Kiro's stand-in rewrites mcp.json, the reload fires, the attacker's command launches "with the developer's privileges." Except inside the VM the developer is the ubuntu user in a throwaway box, and the box holds nothing worth the trip. The code goes looking for the credentials that make the attack pay, the Anthropic key, the AWS keys, the GitHub token, and finds decoys. In Bromure the real secrets never enter the VM; a credential broker on the host injects placeholders like brm_…, a synthetic kubeconfig, and a throwaway SSH key, and swaps the real value in only at the network boundary, on requests to destinations you approved. Source theft reaches only the folders you chose to mount. "Lateral movement into internal systems" reaches the per-profile VM firewall.

Normal setup — one machineHidden page → Kiro rewrites mcp.json →code runs as you, on your MacReal keys & tokenstakenSource & cloudreachablePlanted MCP server persistsacross restartsThe prompt was the only line, and it was crossed.Bromure Agentic Coding — disposable VMSame chain fires — inside the VM,as a throwaway ubuntu userGrabs decoysbrm_… , not real keysEgress gatedat the host proxy, loggedClose the session → box resetsplanted server & persistence goneThe line is the hypervisor, not the prompt.
The same attack, two machines. On a normal setup (left) the injected code runs on the developer's Mac and reaches the real credentials, source, and cloud sessions living beside it. Under Bromure Agentic Coding (right) the identical chain runs inside a disposable VM: the code executes as a throwaway ubuntu user, the credentials it grabs are decoys, its outbound requests are gated and logged at the host proxy, and closing the session resets the box, erasing the planted MCP server and any persistence. The hypervisor is the line the model cannot talk its way across.

Then there is the outbound half. Stealing a decoy only pays if you can send it somewhere, and moving laterally means reaching a second host. Both are network actions, and in Bromure both cross the host proxy, where the true destination of a request is gated on the way out and the destructive shape of an action, a delete or a drop or a terminate against the cloud and git APIs the profile fronts, meets a guardrail that can refuse it, no matter what the agent was talked into doing. Whatever the launched code attempts leaves a logged line in the session trace.

And a layer sits in front of all of this, for a reason that fits this attack exactly. Bromure scores the untrusted content an agent reads, a fetched page or a tool response, with an on-device injection detector before the model acts on it. The reason one-pixel text works is that a person reads the rendered page. The classifier does not; it reads the same raw stream the model reads, where color:#fff;font-size:1px hides nothing. The trick that makes the paragraph invisible to an eye makes no difference to a scorer reading the bytes. It catches most of this, and a novel enough disguise can still slip one detector, so it sits in front of the disposable box rather than replacing it.

The web is the untrusted input now

For years the advice for coding agents was to be careful which repositories you trust. Kiro moves the lesson forward: the dangerous input was a documentation page, the kind an agent reads a hundred times a day, and the weapon was a CSS property a browser has honored since the 1990s. As more of the development workflow runs through agents that read the open web, every page is untrusted input, and no amount of reading it more carefully fixes that.

The prompt AWS added is the right patch, and it should ship. But a control that depends on a person catching the one write that matters, on every page, forever, is a control with a bad long-run record. Bromure Agentic Coding does not ask the agent to be careful with the web. It assumes the page wins, the config gets rewritten, and the code runs, and it makes sure that when all of that happens, it happens in a box with nothing to take and no way home. That is the difference between a prompt you can talk past and a line drawn one level down, where the talking stops working. Install it and give your agent a machine that is not yours to lose.