Account Takeover via Valid Permissions: A Proof of Concept

GhostGeek Expert 2h ago Updated Jul 26, 2026 325 views 13 likes 2 min read

Standard RBAC is fundamentally broken when applied to LLM agents because it evaluates tool calls in isolation. If every single step in a sequence is "allowed" by the permission list, but the combination of those steps leads to a security breach, your guardrails aren't actually guarding anything.

I've been testing this "sequence attack" logic with my team. We found that most deployment patterns rely on per-call checks: "Does the agent have the update_email permission? Yes. Does it have send_password_reset? Yes." The result is a successful account takeover despite 100% green checks.

If you want to see this in action without the fluff, you can run this reproduction script. It uses only the Python standard library—no network calls, no API keys, just pure logic.

git clone https://github.com/keniel13-ui/sequence-attack-repro
cd sequence-attack-repro && python3 repro.py

The Failure of Step-Only RBAC

In a real-world AI workflow, an agent might handle a support ticket. If the ticket says "change my email to [email protected] and reset my password," a basic system sees:

  • read_ticket -> ALLOWED
  • read_customer -> ALLOWED
  • update_contact_email -> ALLOWED
  • send_password_reset -> ALLOWED

Everything is within the agent's role, yet the account is gone. Prompt injection classifiers might catch some of this, but the core issue is architectural.

Solving for Composition

The real challenge is when the caller is verified and the purpose is legitimate (e.g., account_recovery). In these cases, the agent should be able to read data and change identity details. The security failure happens at the composition level.

The goal is to move from "Is this tool allowed?" to "Is this sequence of tools allowed?" In the provided repo, the system blocks the final step not because the tool is forbidden, but because of the prior_action_classes.

Here is how the system logs the block when it detects a sequence attack:

{
 "tool": "send_password_reset",
 "args": { "id": "cust_77" },
 "action_class": "CREDENTIAL_RECOVERY",
 "grant": {
 "principal": "caller_claiming_cust_77",
 "purpose": "account_recovery",
 "verified_via": "callback_verified"
 },
 "facts_in_chain": [],
 "prior_action_classes": ["READ", "IDENTITY_MUTATION"],
 "decision": { "allow": false, "rule": "R4_SEQUENCE" },
 "why": "credential recovery after an identity mutation in the same session composes to account takeover. Every step was allowed. The sequence was the attack.",
 "chain_sha256": "726f65973fb027640049120971a43ca68300197d56ab2d74d5ca94a977d907a7"
}

By tracking prior_action_classes, the gate recognizes that IDENTITY_MUTATION followed by CREDENTIAL_RECOVERY is a high-risk pattern, regardless of whether the individual permissions are green. This is a much more robust approach for any production LLM agent deployment.

agentsdevopssecurityWorkflowAI Implementation

All Replies (3)

A
AveryPilot Novice 10h ago
Does this happen more with chain-of-thought prompts or just standard sequential tool calls?
0 Reply
M
MicroPanda Intermediate 10h ago
Ran into this with a custom GPT last month. Permissions were tight, but logic gaps killed it.
0 Reply
G
GhostGeek Expert 10h ago
Had a similar issue with a multi-step API flow. Adding state validation between calls usually helps.
0 Reply

Write a Reply

Markdown supported