AWS VDP · Report #3632577
Bedrock AgentCore Starter Toolkit Creates Gateway IAM Roles Without Confused Deputy Protections
How it was found
NOTE! Thanks for submitting a report to Amazon Web Services! Please replace all the [square] sections below with the pertinent details. Remember, the more detail you provide, the easier it is for us to triage and respond quickly, so be sure to take your time filling out the report!
Description: The Bedrock AgentCore Starter Toolkit (https://github.com/aws/bedrock-agentcore-starter-toolkit) CLI (agentcore gateway create) creates IAM roles for the AgentCore Gateway with trust policies that lack aws:SourceArn and aws:SourceAccount condition keys. This enables a cross-account confused deputy attack where any AgentCore workload in any AWS account can assume the victim's Gateway role. The role grants access to all secrets in the account, full KMS permissions, the ability to invoke any Lambda function, full DynamoDB access, and read access to all S3 buckets.
Note: The AWS Console correctly includes these conditions when creating a Gateway via the web UI. The vulnerability is specific to the CLI toolkit's code path, which is the primary interface for developers automating infrastructure deployment.
The security intent exists but was not applied consistently: the Jinja2 template at utils/runtime/templates/execution_role_trust_policy.json.j2 correctly includes both conditions, and the AWS Console includes them. The Gateway trust policy constant at src/bedrock_agentcore_starter_toolkit/operations/gateway/constants.py (lines 47-56) does not.
The same missing-condition pattern appears in the CDK template (agentcore-stack.ts.j2, lines 37-40 and 312-314) and Terraform template (bedrock_agentcore.tf.j2, lines 238-247), meaning all infrastructure-as-code deployment paths are affected.
Steps To Reproduce:
1. Install the Bedrock AgentCore Starter Toolkit and create a Gateway:
pip install bedrock-agentcore-starter-toolkit
agentcore gateway create --name my-gateway
2. Inspect the created IAM role's trust policy:
aws iam get-role --role-name <gateway-role-name> --query 'Role.AssumeRolePolicyDocument'
3. Observe the trust policy:
BEDROCK_AGENTCORE_TRUST_POLICY = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
"Action": "sts:AssumeRole",
}
],
}
No Condition block. Compare with the Jinja2 template which correctly includes:
"Condition": {
"StringEquals": {
"aws:SourceAccount": "{{ account_id }}"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:{{ region }}:{{ account_id }}:*"
}
}
This inconsistency confirms the security intent exists but was not applied to the Gateway trust policy constant.
4. Confused deputy exploitation: From a different AWS account with AgentCore access, configure an AgentCore Gateway target using the victim's role ARN. When the attacker's Gateway makes calls, the AgentCore service assumes the victim's role on the attacker's behalf.
The role created with this trust policy is granted (lines 58-102):
bedrock-agentcore:*onarn:aws:bedrock-agentcore:*:*:*secretsmanager:GetSecretValueon*(every secret in the account)lambda:InvokeFunctiononarn:aws:lambda:*:*:function:*(every Lambda)kms:*onarn:aws:kms:*:*:*(full KMS admin including key deletion)AmazonDynamoDBFullAccessmanaged policyAmazonS3ReadOnlyAccessmanaged policy
Impact
Summary:
An attacker with their own AWS account and AgentCore access can:
- Read all secrets in the victim's account via
secretsmanager:GetSecretValueon* - Invoke any Lambda function in the victim's account
- Read all S3 data via
AmazonS3ReadOnlyAccess - Read/write/delete any DynamoDB table via
AmazonDynamoDBFullAccess - Manage KMS keys including disabling or scheduling deletion via
kms:*
The attacker only needs to know the victim's role ARN, which follows a predictable naming pattern generated by the toolkit.
Additionally, the same trust policy pattern (without conditions) appears in the CDK template (agentcore-stack.ts.j2, lines 37-40 and 312-314) and Terraform template (bedrock_agentcore.tf.j2, lines 238-247), affecting users who deploy via infrastructure-as-code.
Recommended Fix
Add condition keys to BEDROCK_AGENTCORE_TRUST_POLICY in constants.py, matching the pattern already used in the Jinja2 trust policy template:
BEDROCK_AGENTCORE_TRUST_POLICY = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "<ACCOUNT_ID>"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:<REGION>:<ACCOUNT_ID>:*"
}
},
}
],
}
Since the constant is used at runtime, the account ID and region should be parameterized (as the Jinja2 template already does).
Apply the same fix to the CDK and Terraform templates.
Additionally, consider scoping down the attached permissions:
secretsmanager:GetSecretValueshould be limited toarn:aws:secretsmanager:*:*:secret:bedrock-agentcore-identity!*kms:*should be scoped to specific actions needed (e.g.,kms:Decrypt,kms:GenerateDataKey)
Disclosure
For awareness, we plan to publish a blog post detailing these findings after remediation. We are happy to share a draft ahead of publication and coordinate on timing.