Rogue Agent: One Permission Hijacked Every Chatbot in the Project
Varonis found a Dialogflow CX flaw where one edit permission on one agent could read conversations, steal credentials, and impersonate the bot. The failure was plumbing, not prompts.
One IAM permission on one agent was enough to hijack every chatbot in a Google Cloud project. That is the finding Varonis Threat Labs disclosed this month in Dialogflow CX, Google’s platform for building customer-service chatbots and voice agents. They named it Rogue Agent.
Every company wants an AI chatbot that talks to customers, answers questions, and connects to real business systems. Rogue Agent shows what happens when that chatbot starts reading conversations, harvesting credentials, and impersonating itself.
What happened
- Varonis found the vulnerability inside Dialogflow CX’s Code Blocks feature, which lets developers add custom Python logic to chatbot workflows.
- An attacker holding a single permission,
dialogflow.playbooks.update, could inject persistent malicious code into the agent’s execution pipeline. - Google issued an initial fix in April 2026 and fully resolved the issue in June 2026. Varonis reported the flaw in November 2025.
- Varonis says it is not aware of any exploitation in the wild before the patch.
The core issue was not “the AI got tricked.” It was worse and more boring: the plumbing around the AI trusted the wrong thing.
How the plumbing failed
Code Blocks run inside a Google-managed Cloud Run environment. According to Varonis, all agents in the same GCP project shared that single runtime, and it had public network egress, a writable filesystem, and elevated execution privileges.
The specific hole: a writable file named code_execution_env.py, the file responsible for executing Playbook Code Blocks through Python’s exec(). Overwrite it and you own the pipeline. From there an attacker could:
- Read the
historyvariable: full conversation records and user utterances. - Read the
statevariable: session IDs and session parameters. - Call internal functions like
respond()to force the chatbot to return attacker-chosen messages. - Exfiltrate conversation data to an external server, since the runtime had open egress.
In plain English: a compromised bot could ask users to “reauthenticate,” collect the credentials, and make the whole exchange look like a normal chatbot response. Customer support with Ocean’s Eleven energy.
The detection story is the worst part. Per Varonis, Cloud Logging did not record the overwrite or the injected logic. The tampering was near-invisible after the fact.
Technical walkthrough: how one permission became project-wide code execution
Strip away the branding and Rogue Agent is a trust-boundary collapse. Five things had to line up, and they did.
Step 1: The permission that looked narrow
Dialogflow CX uses Playbooks (its agent-orchestration primitive) that can embed Code Blocks: custom Python that runs inside a conversation flow. To edit a Playbook you need dialogflow.playbooks.update.
On paper that reads like a content-authoring right, the kind you would hand a bot developer without much thought. In practice it is a code-deployment right. Editing a Playbook means editing code that will run server-side, in Google’s environment, on live traffic. The permission’s name described the object it touched (a Playbook), not the capability it actually conferred (arbitrary Python execution). That gap between named scope and real capability is the whole attack.
Step 2: The shared runtime
Code Blocks do not run in a per-agent sandbox. They run inside a single Google-managed Cloud Run service shared by every agent in the GCP project. One runtime, one filesystem, one process context, N agents.
That runtime had three properties an attacker wants:
| Property | What it gave the attacker |
|---|---|
| Writable filesystem | A place to persist a payload |
| Public network egress | A path to exfiltrate what the payload collected |
| Elevated execution privileges | Authority to touch other agents’ execution |
Shared-tenancy inside your own project sounds harmless. It means the blast radius of one compromised agent is every agent next to it.
Step 3: The writable interpreter
The specific file was code_execution_env.py, the module responsible for executing Playbook Code Blocks through Python’s exec(). It was writable from inside the shared runtime.
This is the pivot. A Code Block is supposed to be executed by the interpreter. Because the interpreter file itself was writable, a Code Block could rewrite the thing that runs it. You stop being a tenant of the runtime and become its operator. Every Code Block that any agent runs afterward flows through your modified exec() path.
Normal: Playbook Code Block → code_execution_env.py (exec) → result
Compromised: attacker overwrites code_execution_env.py once
→ every future Code Block, every agent → attacker's exec path
Step 4: The variables already in scope
Once you own the execution path, the useful data is handed to you. The runtime exposes two variables to Code Blocks:
history: full conversation records and user utterances.state: session IDs and session parameters.
And it exposes internal functions, including respond(), which sets what the chatbot says back to the user. An attacker who controls the execution path controls all three. Read history and state, ship them out the open egress, then call respond() to steer the conversation.
The credential-phishing move is the sharp end. Call respond("Your session expired, please sign in again"). To the user it is indistinguishable from a real prompt, because it is a real response from the real bot. There is no spoofed domain, no lookalike page, no injected iframe. The legitimate agent is asking, using the legitimate channel. The captured credentials go out the same egress as the conversation logs.
Step 5: The silence
The reason this rates as serious rather than clever: Cloud Logging recorded neither the overwrite of code_execution_env.py nor the injected logic. The platform’s own audit trail had a blind spot exactly where the tampering happened.
So the defender’s position was: the malicious code runs with legitimate privileges, produces legitimate-looking responses, and leaves no record in the log you would check. The only forensic residue Varonis could point defenders to was indirect: DATA_WRITE events on Playbooks/UpdatePlaybook, and clusters of failed user requests. You reconstruct the crime from the edit that preceded it and the errors that followed it, because the act itself was invisible.
Why prompt filters would not have caught any of this
Notice what never happened: nobody jailbroke the model. No adversarial prompt, no injection through a user utterance, no clever system-prompt leak. The model behaved normally the entire time. The compromise lived one layer down, in the code-execution substrate and the IAM model around it.
This is the pattern we keep flagging. The 2026 agent attacks that matter are not “trick the model” attacks. They are “the infrastructure around the model trusted the wrong thing” attacks: a writable interpreter, a shared runtime, a permission whose name undersold its power, an audit log with a hole in it. A perfect prompt-injection classifier stops zero percent of Rogue Agent.
Why this matters
Enterprise AI agents are moving from “answer this FAQ” to “touch customer data, trigger workflows, call backend systems.” That makes agent permissions the new security boundary. If one lightly protected workflow can see too much, write too much, or share a runtime with every other agent in the project, the chatbot becomes a front door with a very confident welcome mat.
If you run Dialogflow CX, Varonis recommends three checks:
- Review audit logs for
DATA_WRITEevents with methodgoogle.cloud.dialogflow.cx.v3.Playbooks/UpdatePlaybook. - Query Cloud Logging for failed user requests around Playbook updates.
- Manually audit Code Block configurations for code you did not write.
Our take
Agent security will not be solved by better prompts. It will be solved by boring controls: narrow permissions, isolated runtimes, visible logs, and default skepticism toward any agent that can execute code.
Rogue Agent is a clean case study in the gap we work on. Three structural facts did the damage:
- One permission carried project-wide authority. An edit right on one agent became code execution across all of them. Nothing checked whether the capability that flowed matched the capability that was granted.
- The runtime kept no independent record. The platform’s own logs missed the tampering, so the operator had no way to prove what the agent ran versus what was authorized.
- The agent’s output carried no provenance. A forced
respond()call looked identical to a legitimate answer. Users and downstream systems had no way to tell them apart.
Google patched its layer, and credit to both Google and Varonis for a clean disclosure. But every agent platform has this layer, and the structural gap repeats across all of them: there is no cross-vendor chain of custody for what an agent was authorized to do, what it did, and who can prove the two match. That record has to live outside the runtime it describes, because Rogue Agent shows what happens when the runtime is the only witness.
The next enterprise AI race is not who deploys agents fastest. It is who can prove their agents did not become the intern from hell with production access.
SURADAR builds chain-of-custody assurance for agent delegation: cryptographic records of who authorized what, independent of the platform running the agent. If you are deploying agents that touch customer data, talk to us.