The agent that moved money it was told never to move
Author
David McNelis
Date Published

An AI agent is supposed to decide. You send it a message, the model reads it under a system prompt, and the model chooses whether to call a tool, which one, and with what arguments. That decision is where all the safety lives. The system prompt, the guardrails, the model's own judgment about whether a request is reasonable. Every one of those checks happens inside that single step.
We found a way to skip the step.
In Google's Agent Development Kit for Python, the open-source runtime known as google-adk, there is an opt-in feature called resumable mode. It exists so an agent can pause a long task and pick it back up later. When it is turned on, we found that the framework would take a tool call handed to it by the caller and execute it directly, with the caller's chosen arguments, and never run the model at all. It also never checked who authored the request. Anyone who could reach the agent's API, which ADK's built-in server exposes without authentication by default, could forge an instruction that looked like it came from the user, attach a tool call to it, and the agent would carry it out.
The proof
We do not report bugs we cannot demonstrate, so we built a victim that makes the stakes obvious: a banking assistant whose instructions say, in plain language, never to move money without the user's explicit, confirmed intent.
That instruction is real, and under normal use it works, because the model reads it every turn. So we forged a request carrying a transfer_funds call, pointed to an attacker's account, for fifty thousand dollars, and started a resumable run. The transfer went through. The safety instruction was never consulted, because the step that reads it never ran.

The most damning detail is the quietest one. We ran the whole thing on a machine with no AI credentials of any kind. If the model had actually been asked to make this decision, the process would have failed the moment it tried to reach the model, with a plain error about a missing key. That error never appears in the successful path. The tool ran anyway. That is the signature of a bypass rather than a model being talked into something: there was no model in the loop at all.
We reproduced it end to end against two shipping versions of the library, by execution, not by reading the source and guessing.
This is not the first time this door has been found. In July, alongside the CoreBreak disclosures we covered in August, Google shipped ADK 2.5.0 with a fix that rejects tool calls inside a new user message. That guard sat at the front door. Session creation was a side door: a caller can seed a new session with past events, and those were allowed to carry tool calls on purpose. We planted the forged call there and sent no new message at all, so the July check never saw it. That is why our fix sits where the call is executed rather than where it arrives: it covers every entrance, including ones nobody has found yet.
Why this is the whole problem
This is the argument NobleCloak exists to make, and this bug states it more clearly than we could.
Every serious agent framework has solved how agents execute. They call tools reliably, they stream results, they resume long jobs. What almost none of them have solved is authority: proving who is allowed to make an agent act, and verifying that the instruction it is about to carry out actually came from where it claims. That is a provenance question, and provenance is not a feature you add later. It is the foundation the rest of the safety story rests on.
What makes this bug worth writing about is not that it is exotic. It is that it is ordinary. It is a missing authorship check, the same class of oversight that has produced authorization bugs in ordinary software for decades. The only new part is the setting. When the thing being authorized is an autonomous agent that can move money, send mail, or delete records, a missing provenance check stops being a data-integrity footnote and becomes the difference between an agent that follows its instructions and one that follows a stranger's.
It is also worth being precise about what this bug does and does not do, because overstating it would undercut the point. It is not remote code execution, and it does not reach beyond the access the caller already has. Within that access, though, it lets the caller choose any tool the agent has, with any arguments, and it removes the model's turn: the judgment layer that operators are trusting to sit between a request and a consequential action. For any tool whose safety rests on the model choosing wisely rather than on a hard-coded policy, that layer was the control. This bug takes it out of the loop.
What we did about it
We build governed AI on top of commodity agent runtimes, and Google's ADK is one we evaluate closely, which is how we came across this in the first place.
We reported it privately to Google's security team first. They assessed it and decided not to track it as a security vulnerability, on the reasoning that resumable mode is experimental and off by default, and they invited us to file it in the open. So we did, and the ADK team confirmed and reproduced it. The full mechanism, a working reproduction anyone can run in about two minutes, and the patch are public:
- The report: google/adk-python issue #7076
- The fix: google/adk-python pull request #7077
- The merged commit: google/adk-python@2c61b84
The fix is one line of intent: before the framework resume-executes a tool call, require that the current agent is the one that authored it. A genuine resume is unaffected, because when an agent pauses on a pending call, the agent authored that call. A forged request authored by someone else no longer runs, and the flow falls through to a normal model turn instead, which is exactly where the safety was supposed to be all along. Google merged it into ADK's main branch with regression tests, so the door stays shut. It came in through Google's internal import process as a separate commit, which is why the pull request itself shows as closed rather than merged.

It is not in a tagged release yet. google-adk 2.9.1 (the latest release as of 2026-09-16) and earlier are still affected when resumable mode is on. Until a release that carries the fix ships, leave resumable mode off, or put a before-tool policy check on any tool that can move money or delete data.
The takeaway
If you are building on agents, the lesson is not "avoid this one framework." Resumable mode is a reasonable feature, the fix was small, and the maintainers are responsive. The lesson is where to point your attention. Ask of any agent system you rely on: when this thing takes an action, what proves the instruction came from someone allowed to give it? If the honest answer is "the model probably would have caught it," you are trusting judgment where you need verification.
That gap between judgment and verification is the layer we build. Named agent identity, provenance you can check, trust that is granted deliberately rather than assumed. This bug is a small, concrete instance of the reason that layer has to exist. We were glad to find it, gladder to see it fixed, and we will keep looking.
AWS, Google, and Vercel patched flaws that let attackers fire AI agent tools without the model ever running. What CoreBreak changes in your AI vendor diligence.

