Writing
Identity & Privilege Abuse: When Your Agent Acts Like It Owns the Place (ASI03)
On this page
This is post #3 of the OWASP Agentic AI Top 10: What Builders on AWS Need to Know series.
Your engineering manager builds an agent to help her team. She gives it access to the team’s Jira board, their code repos, and the internal wiki. Normal stuff.
Then the agent delegates a subtask to a “helper” agent. And that helper inherits all of the manager’s permissions. The full repo access. The HR data she can see. The production deployment keys cached in the session.
Nobody asked for this. Nobody approved it. It just… happened. Because the delegation didn’t scope down the permissions.
Now imagine an attacker discovers this pattern. They don’t need to compromise the manager’s account. They just need to compromise the least-privileged agent in the chain, and they can relay instructions upstream to the one with all the keys.
Welcome to Identity & Privilege Abuse.
What Is Identity & Privilege Abuse?
ASI03 is the #3 threat in the OWASP Top 10 for Agentic Applications. It exploits the fundamental mismatch between user-centric identity systems and agentic architectures.
At its core: agents inherit, accumulate, or escalate privileges in ways that bypass the access controls you designed for humans.
Here’s why it’s different in agentic systems:
- A human user has one identity, one session, one set of permissions
- An agent can delegate to other agents, cache credentials across sessions, inherit trust from its creator, and accumulate permissions through chains of delegation
The classic computer science term for this is the confused deputy problem. And agents are the most confused deputies we’ve ever built.
How Does It Actually Happen?
OWASP identifies five common patterns. Every single one of them is showing up in production systems right now.
1. Un-Scoped Privilege Inheritance
A high-privilege agent delegates a task to a worker agent. For convenience (or because the framework doesn’t support it), it passes its full access context along with the task. The worker now has permissions it never should have had.
Real example: A finance agent delegates to a “DB query” agent but passes all its permissions. An attacker steering the query prompts uses the inherited access to exfiltrate HR and legal data that the query agent should never have seen.
Real incident: CVE-2026-35435 disclosed in May 2026. A critical elevation-of-privilege vulnerability in Azure AI Foundry allowed M365 published agents to escalate access due to improper access control. Microsoft mitigated it server-side, but the root cause was the same pattern: agent permissions weren’t scoped independently of the creating user’s context.
2. Memory-Based Privilege Retention
Credential leaks aren’t new. I wrote about the anatomy of an AWS key leak way back in 2019. But agents accelerate the blast radius because they act on those credentials autonomously, at machine speed.
Agents cache credentials, keys, or session data for context reuse. If memory isn’t segmented or cleared between tasks, attackers can prompt the agent to reuse cached secrets from a prior high-privilege session.
Real example: An IT admin agent caches SSH credentials during a patching workflow. A non-admin user reuses the same session and prompts it to use those credentials to create an unauthorized account.
Real incident: CVE-2026-13437 in PowerShell Universal. An authenticated user with AI Agent read access could retrieve stored OAuth tokens and reuse them for authentication. The exposed tokens carried higher privileges than the original caller, enabling privilege escalation through credential reuse. Exactly the memory-based retention pattern, just in a different framework.
3. Cross-Agent Trust Exploitation (Confused Deputy)
In multi-agent systems, agents often trust internal requests by default. A compromised low-privilege agent relays valid-looking instructions to a high-privilege agent. The high-privilege agent executes them without re-checking the original user’s intent.
Real example: Claude in Chrome exposed exactly this. A zero-permission browser extension could inject prompts, bypass confirmations, and trigger cross-site actions including email, Drive, and GitHub access. The agent inherited user privileges because browser trust boundaries were too loose.
The SANS blog put it bluntly: “Enterprise AI agents are the newest, and potentially the most dangerous confused deputies in your cloud environment.”
Real incident: The Google Dialogflow CX “Rogue Agent” vulnerability (Varonis, July 2026) demonstrated this at scale. A user with a single permission (dialogflow.playbooks.update) on one agent could inject persistent malicious code that compromised every other agent in the same Google Cloud project. One confused deputy gave access to all the others. Reported November 2025, fully fixed June 2026.
4. Time-of-Check to Time-of-Use (TOCTOU)
Permissions get validated at workflow start. Hours later, the user’s access changes (or gets revoked). But the agent continues with the old authorization token, completing actions the user no longer has rights to approve.
5. Synthetic Identity Injection
Attackers register fake agents with fabricated identities in internal registries. Other agents, trusting the descriptors, route privileged tasks to the attacker-controlled agent.
Real example from OWASP: An attacker registers a fake “Admin Helper” agent in an Agent2Agent registry with a forged agent card. Other agents route system-level maintenance tasks to it under assumed internal trust.
Why Traditional IAM Doesn’t Fully Solve This
If you’re thinking “I already have IAM roles, this is handled” - hold that thought.
Traditional IAM was designed for human users and stateless services. It answers: “Can this identity perform this action on this resource?”
But agents introduce questions IAM wasn’t built to answer:
- Can this agent act on behalf of this specific user for this specific task? (Not just “does the role allow it”)
- Did the user actually intend this delegation? (The agent might have been tricked)
- Has the user’s context changed since this workflow started? (TOCTOU)
- Is this agent-to-agent delegation legitimate? (Confused deputy)
You need IAM plus a purpose-aware authorization layer. That’s where Cedar comes in.
Mitigating Identity & Privilege Abuse on AWS
1. Amazon Verified Permissions (Cedar) for Agent Authorization
AWS just published Enforce least-privilege authorization in multi-agent AI chains using Cedar. This is the key architectural pattern for ASI03.
Amazon Verified Permissions lets you write fine-grained authorization policies in Cedar that answer the questions IAM can’t:
// ---------------------------------------------------------------------------
// Guardrail: never allow deep delegation chains for ReadOrder.
// A forbid overrides ANY permit, so this genuinely stops escalation.
// Fails closed: denies if the depth is missing or greater than 2.
// `delegationDepth` is a Long that your enforcement point (PEP) computes
// and puts into context - Cedar cannot measure a chain itself.
// ---------------------------------------------------------------------------
forbid(
principal,
action == Action::"ReadOrder",
resource
)
when {
!(context has delegationDepth) || context.delegationDepth > 2
};
// ---------------------------------------------------------------------------
// Grant: the order-lookup agent may read ONLY the requesting user's orders,
// and ONLY when the declared purpose is an order status check.
// `has` guards prevent runtime errors if any of these context fields are
// optional in your schema. If they are marked required, the guards are
// harmless but unnecessary.
// ---------------------------------------------------------------------------
permit(
principal == Agent::"order-lookup-agent",
action == Action::"ReadOrder",
resource
)
when {
context has requestingUser
&& context has taskPurpose
&& resource.customerId == context.requestingUser.id
&& context.taskPurpose == "order_status_check"
};
Two things you must confirm on your side for this to validate and behave:
- Your PEP populates
context.delegationDepthas a number, not a set or string. This replaces the brokencontext.delegationChain.length. resource.customerIdandcontext.requestingUser.idare the same type (bothStringin the usual setup). IfrequestingUseris actually an entity reference, swap that line forresource.owner == context.requestingUserinstead.
2. Session Policies for Scope-Down
When an agent assumes a role, use STS inline session policies to narrow permissions to only what the current task needs:
import json
import boto3
sts = boto3.client("sts")
# Per task, the agent assumes its base role with a scope-down session policy.
response = sts.assume_role(
RoleArn="arn:aws:iam::123456789012:role/finance-agent-base",
RoleSessionName=f"task-{task_id}-user-{user_id}",
DurationSeconds=900, # 15 minutes (also the minimum assume_role allows)
Policy=json.dumps({
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "dynamodb:Query", # Query only — this is load-bearing, see below
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/orders",
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [user_id]
}
}
}]
})
)
Even if the agent’s base role allows broad DynamoDB access, the session policy limits it to this user’s records only, for 15 minutes only. Delegation to a sub-agent doesn’t widen the scope - it can only narrow further.
3. Permission Boundaries to Prevent Self-Escalation
Use IAM permission boundaries to set a hard ceiling that agents can never exceed, even if they somehow modify their own policies:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"ses:SendEmail"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:*",
"sts:AssumeRole"
],
"Resource": "*"
}
]
}
The session’s effective permissions are the intersection of the base role’s identity policy and this session policy, so the policy can only remove access, never add it. Even if finance-agent-base grants broad DynamoDB access, this session can do exactly one thing for the next 15 minutes: run Query against the orders table, and only for items whose partition key equals user_id.
Two conditions make that guarantee hold:
- The
orderstable must be partitioned byuser_id.dynamodb:LeadingKeysmatches on the partition key, so this only isolates a user’s data ifuser_idis that table’s partition key. - Restricting the action to
Queryis deliberate, not incidental.ForAllValuesevaluates to true when a request carries no leading keys, and aScancarries none — so allowingScanhere would expose every user’s records. Query always specifies the partition key, which is what keeps the condition meaningful.
Delegation inherits the same ceiling: as long as a sub-agent reuses these credentials (or re-assumes with an equal-or-tighter policy), it can only narrow the scope further, never widen it. Chaining into a different role, though, starts fresh from that role’s own permissions — session policies don’t carry across role chaining, so enforce the boundary explicitly if your design depends on it.
4. Service Control Policies (Organizational Guardrails)
Service Control Policies give you organizational-level hard stops that no agent (and no human) can bypass:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AgentsCannotCreateUsersOrInstances",
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"ec2:RunInstances"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalTag/agent-tier": "admin"
}
}
}]
}
And one other thing you should note. Since the operator is StringNotEquals, a principal with no agent-tier tag at all gets denied too. Absent tag, deny fires. It fails closed, which is exactly what you want from a guardrail and the opposite of what most people assume.
5. Per-Action Re-Authorization with Verified Permissions
Check once, trust forever? No.
You badge in at 9am. At 11am HR revokes your access. But the door you badged at 9am already said yes, so you roam the building all afternoon.
Common pattern: the agent checks “is this user allowed” at the top of the workflow, gets a yes, then runs for the next twenty minutes on that one yes.
That is a TOCTOU (https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) problem. Time Of Check to Time Of Use. The gap between “I checked” and “I acted” is where the bad things live, and in an agentic workflow that gap is minutes or hours.
So the fix is simple. Re-check at every privileged step, not once at the start. Here it is with Amazon Verified Permissions (https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/what-is-avp.html?trk=d76afd77-bb62-46ac-b0a3-9dbf5ecde253):
import boto3
from datetime import datetime, timezone
avp = boto3.client("verifiedpermissions")
def authorize_agent_action(agent_id, user_id, action, resource, context):
"""Re-authorize every privileged action, not just at session start."""
response = avp.is_authorized(
policyStoreId="ps-abc123",
principal={"entityType": "Agent", "entityId": agent_id},
action={"actionType": "Action", "actionId": action},
resource={"entityType": "Order", "entityId": resource},
context={
"contextMap": {
"requestingUser": {"entityIdentifier": {"entityType": "User", "entityId": user_id}},
"currentTime": {"string": datetime.now(timezone.utc).isoformat()},
"delegationDepth": {"long": context.get("delegation_depth", 0)}
}
}
)
if response["decision"] != "ALLOW":
raise PermissionError(
f"Agent {agent_id} denied: {action} on {resource} for user {user_id}"
)
If the answer is not a flat ALLOW, we raise and stop. Anything that is not an explicit yes is a no. Fail closed. Pull a user’s permission mid-workflow and the next step bounces.
But here is the catch. Re-checking is only as good as what you check against.
Say the door re-checks your badge every time, but against an access list photocopied at 9am and stuffed in a drawer. HR revoked you at 11am. The printout still says you are fine, so the door waves you through all day. It is reading yesterday’s news.
That is what happens when you feed Verified Permissions stale data. It does not look up your user’s current group membership. You pass it in through entities. Load that once at session start and keep reusing it, and you have not closed the gap. You moved it from the token to your cache. The re-check only catches a change that lives where you reada afresh every call: the policies in your store, or entity data you re-fetch each time. Anything else and the loop is theater.
So re-authorize every step, yes. Just remember the authorizer is only as smart as the data you feed it. Give it fresh, or do not bother.
6. Credential Isolation Between Sessions
Wipe agent memory between user sessions. Amazon Bedrock AgentCore Runtime handles this for you. Each agent invocation runs in an isolated execution environment with session-scoped state that doesn’t bleed across users. If you’re building your own orchestration, use separate Lambda execution environments or Fargate tasks per user session. Either way: never reuse an agent instance that handled a privileged user’s data for a less-privileged user’s request.
Key Takeaway
Agents are not users. They don’t get to inherit a user’s full identity just because they’re acting “on behalf of” that user. Every delegation must scope down. Every action must re-authorize. Every session must isolate.
The confused deputy problem isn’t new. But AI agents are the most powerful, most autonomous, most broadly-permissioned confused deputies we’ve ever deployed. Treat them accordingly.
Up Next
Post 4: Agentic Supply Chain Vulnerabilities (ASI04) - Your agent is only as secure as its weakest plugin. Compromised MCP servers, poisoned tool descriptors, and how Lambda layers, Inspector, and PrivateLink help you lock down the supply chain.
I would be very interested to hear your thoughts or comments, so please feel free to ping me on LinkedIn or Twitter. If you’ve dealt with agent privilege escalation in production, I genuinely want to hear how you handled it.
Onward!!