feat(iam): add AWS Bedrock AgentCore privilege escalation paths (#11726)

This commit is contained in:
Sergio Garcia
2026-07-01 06:49:34 -04:00
committed by GitHub
parent a212916a49
commit 1e1c1c018b
4 changed files with 424 additions and 0 deletions
+1
View File
@@ -22,6 +22,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- CIS Google Cloud Platform Foundation Benchmark v5.0.0 compliance framework for the GCP provider [(#11714)](https://github.com/prowler-cloud/prowler/pull/11714)
- CIS Kubernetes Benchmark v2.0.1 compliance framework for the Kubernetes provider [(#11722)](https://github.com/prowler-cloud/prowler/pull/11722)
- CIS GitHub Benchmark v1.2.0 compliance framework for the GitHub provider [(#11719)](https://github.com/prowler-cloud/prowler/pull/11719)
- AWS Bedrock AgentCore privilege escalation paths in the IAM privilege escalation checks, covering Runtime, Harness, Code Interpreter and Custom Browser [(#11726)](https://github.com/prowler-cloud/prowler/pull/11726)
- `--scan-secrets-validate` flag and `aws.secrets_validate` configuration option to optionally validate the secrets discovered by the secret-scanning checks against the provider APIs; secrets confirmed to be live are reported as critical [(#11694)](https://github.com/prowler-cloud/prowler/pull/11694)
### 🔄 Changed
@@ -19,6 +19,7 @@ from prowler.providers.aws.services.iam.lib.policy import get_effective_actions
# - https://github.com/RhinoSecurityLabs/Security-Research/blob/master/tools/aws-pentest-tools/aws_escalate.py
# - https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation/
# - https://github.com/DataDog/pathfinding.cloud (AWS IAM Privilege Escalation Path Library)
# - https://www.beyondtrust.com/blog/entry/aws-agentcore-privilege-escalation (AWS Bedrock AgentCore)
privilege_escalation_policies_combination = {
# IAM self-escalation and policy manipulation
@@ -299,6 +300,7 @@ privilege_escalation_policies_combination = {
"PassRole+AgentCoreCreateInterpreter+InvokeInterpreter": {
"iam:PassRole",
"bedrock-agentcore:CreateCodeInterpreter",
"bedrock-agentcore:StartCodeInterpreterSession",
"bedrock-agentcore:InvokeCodeInterpreter",
},
# Prerequisite: Existing Bedrock code interpreter with admin role
@@ -306,6 +308,40 @@ privilege_escalation_policies_combination = {
"bedrock-agentcore:StartCodeInterpreterSession",
"bedrock-agentcore:InvokeCodeInterpreter",
},
# Prerequisite: Existing AgentCore Runtime or Harness with admin execution role.
# InvokeAgentRuntimeCommand runs shell commands as root inside the microVM and
# reads the execution role credentials from MMDS, bypassing the agent and guardrails.
"AgentCoreInvokeRuntimeCommand": {
"bedrock-agentcore:InvokeAgentRuntimeCommand",
},
"PassRole+AgentCoreCreateRuntime+InvokeRuntimeCommand": {
"iam:PassRole",
"bedrock-agentcore:CreateAgentRuntime",
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:InvokeAgentRuntimeCommand",
},
"PassRole+AgentCoreCreateHarness+InvokeRuntimeCommand": {
"iam:PassRole",
"bedrock-agentcore:CreateHarness",
"bedrock-agentcore:CreateAgentRuntime",
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:GetAgentRuntime",
"bedrock-agentcore:InvokeAgentRuntimeCommand",
},
# Prerequisite: Existing AgentCore Custom Browser with admin execution role.
# A remote CDP driver on the browser session reads the role credentials from MMDS.
"AgentCoreBrowserSessionConnect": {
"bedrock-agentcore:StartBrowserSession",
"bedrock-agentcore:ConnectBrowserAutomationStream",
},
"PassRole+AgentCoreCreateBrowser+ConnectBrowser": {
"iam:PassRole",
"bedrock-agentcore:CreateBrowser",
"bedrock-agentcore:StartBrowserSession",
"bedrock-agentcore:ConnectBrowserAutomationStream",
},
# TO-DO: We have to handle AssumeRole just if the resource is * and without conditions
# "sts:AssumeRole": {"sts:AssumeRole"},
}
@@ -1239,6 +1239,7 @@ class Test_iam_inline_policy_allows_privilege_escalation:
"Action": [
"iam:PassRole",
"bedrock-agentcore:CreateCodeInterpreter",
"bedrock-agentcore:StartCodeInterpreterSession",
"bedrock-agentcore:InvokeCodeInterpreter",
],
"Resource": "*",
@@ -1286,6 +1287,10 @@ class Test_iam_inline_policy_allows_privilege_escalation:
assert search(
"bedrock-agentcore:CreateCodeInterpreter", result[0].status_extended
)
assert search(
"bedrock-agentcore:StartCodeInterpreterSession",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:InvokeCodeInterpreter", result[0].status_extended
)
@@ -928,6 +928,7 @@ class Test_iam_policy_allows_privilege_escalation:
"Action": [
"iam:PassRole",
"bedrock-agentcore:CreateCodeInterpreter",
"bedrock-agentcore:StartCodeInterpreterSession",
"bedrock-agentcore:InvokeCodeInterpreter",
],
"Resource": "*",
@@ -973,10 +974,391 @@ class Test_iam_policy_allows_privilege_escalation:
assert search(
"bedrock-agentcore:CreateCodeInterpreter", result[0].status_extended
)
assert search(
"bedrock-agentcore:StartCodeInterpreterSession",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:InvokeCodeInterpreter", result[0].status_extended
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_invoke_runtime_command(
self,
):
"""Test detection of AgentCore Runtime/Harness privilege escalation via InvokeAgentRuntimeCommand on an existing resource."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_invoke_runtime_command_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock-agentcore:InvokeAgentRuntimeCommand",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert result[0].region == AWS_REGION_US_EAST_1
assert result[0].resource_tags == []
assert search(
f"Custom Policy {policy_arn} allows privilege escalation using the following actions: ",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:InvokeAgentRuntimeCommand",
result[0].status_extended,
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_passrole_create_runtime(
self,
):
"""Test detection of AgentCore Runtime privilege escalation by creating a new runtime with a passed role."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_create_runtime_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"bedrock-agentcore:CreateAgentRuntime",
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:InvokeAgentRuntimeCommand",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert search("iam:PassRole", result[0].status_extended)
assert search(
"bedrock-agentcore:CreateAgentRuntime",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:CreateWorkloadIdentity",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:InvokeAgentRuntimeCommand",
result[0].status_extended,
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_passrole_create_harness(
self,
):
"""Test detection of AgentCore Harness privilege escalation by creating a new harness with a passed role."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_create_harness_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"bedrock-agentcore:CreateHarness",
"bedrock-agentcore:CreateAgentRuntime",
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
"bedrock-agentcore:CreateWorkloadIdentity",
"bedrock-agentcore:GetAgentRuntime",
"bedrock-agentcore:InvokeAgentRuntimeCommand",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert search("iam:PassRole", result[0].status_extended)
assert search("bedrock-agentcore:CreateHarness", result[0].status_extended)
assert search(
"bedrock-agentcore:CreateAgentRuntime",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:CreateAgentRuntimeEndpoint",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:CreateWorkloadIdentity",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:GetAgentRuntime", result[0].status_extended
)
assert search(
"bedrock-agentcore:InvokeAgentRuntimeCommand",
result[0].status_extended,
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_browser_session_connect(
self,
):
"""Test detection of AgentCore Custom Browser privilege escalation via an existing browser session."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_browser_session_connect_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock-agentcore:StartBrowserSession",
"bedrock-agentcore:ConnectBrowserAutomationStream",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert search(
"bedrock-agentcore:StartBrowserSession", result[0].status_extended
)
assert search(
"bedrock-agentcore:ConnectBrowserAutomationStream",
result[0].status_extended,
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_passrole_create_browser(
self,
):
"""Test detection of AgentCore Custom Browser privilege escalation by creating a new browser with a passed role."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_create_browser_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"bedrock-agentcore:CreateBrowser",
"bedrock-agentcore:StartBrowserSession",
"bedrock-agentcore:ConnectBrowserAutomationStream",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert search("iam:PassRole", result[0].status_extended)
assert search("bedrock-agentcore:CreateBrowser", result[0].status_extended)
assert search(
"bedrock-agentcore:StartBrowserSession", result[0].status_extended
)
assert search(
"bedrock-agentcore:ConnectBrowserAutomationStream",
result[0].status_extended,
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_agentcore_wildcard(
self,
):
"""Test detection of AgentCore privilege escalation when the policy grants the bedrock-agentcore:* namespace wildcard."""
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
policy_name = "agentcore_wildcard_policy"
policy_document = {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"bedrock-agentcore:*",
],
"Resource": "*",
}
],
}
policy_arn = iam_client.create_policy(
PolicyName=policy_name, PolicyDocument=dumps(policy_document)
)["Policy"]["Arn"]
from prowler.providers.aws.services.iam.iam_service import IAM
with (
mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=aws_provider,
),
mock.patch(
"prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation.iam_client",
new=IAM(aws_provider),
),
):
from prowler.providers.aws.services.iam.iam_policy_allows_privilege_escalation.iam_policy_allows_privilege_escalation import (
iam_policy_allows_privilege_escalation,
)
check = iam_policy_allows_privilege_escalation()
result = check.execute()
assert len(result) == 1
assert result[0].status == "FAIL"
assert result[0].resource_id == policy_name
assert result[0].resource_arn == policy_arn
assert search(
"bedrock-agentcore:InvokeAgentRuntimeCommand",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:StartCodeInterpreterSession",
result[0].status_extended,
)
assert search(
"bedrock-agentcore:StartBrowserSession", result[0].status_extended
)
@mock_aws
def test_iam_policy_allows_privilege_escalation_iam_put(
self,