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: 2 additions & 0 deletions cmd/plabs/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package main

import (
"fmt"
"os"

"github.com/DataDog/pathfinding-labs/internal/cmd"
)

func main() {
if err := cmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
}
6 changes: 6 additions & 0 deletions flags.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ flags:
batch-003-to-admin: "flag{batch_003_admin_captured}"
braket-001-to-admin: "flag{braket_001_admin_captured}"
bedrock-002-to-admin: "flag{bedrock_002_admin_captured}"
bedrock-003-to-admin: "flag{bedrock_003_admin_captured}"
bedrock-004-to-admin: "flag{bedrock_004_admin_captured}"
bedrock-005-to-admin: "flag{bedrock_005_admin_captured}"
bedrock-006-to-admin: "flag{bedrock_006_admin_captured}"
bedrock-007-to-admin: "flag{bedrock_007_admin_captured}"
cognito-identity-001-to-admin: "flag{cognito-identity-pool-unauthenticated-role-swap}"
cloudformation-001-to-admin: "flag{cloudformation_001_admin_captured}"
cloudformation-002-to-admin: "flag{cloudformation_002_admin_captured}"
Expand Down Expand Up @@ -143,4 +148,5 @@ flags:
sts-001-to-admin: "flag{sts_001_admin_captured}"
sts-001-to-bucket: "flag{sts_001_bucket_accessed}"
sts-001-to-ecs-002-to-admin-to-admin: "flag{sts_001_ecs_002_multi_hop_admin_captured}"
sts-role-chain-to-admin: "flag{role_chain_complete}"
sysdig-8-minutes-to-admin-to-admin: "flag{sysdig_8min_admin_captured}"
6 changes: 6 additions & 0 deletions internal/aws/slr.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type ServiceLinkedRoleStatus struct {
EMRExists bool
EMRServerlessExists bool
ImageBuilderExists bool
AgentCoreExists bool
BatchExists bool
}

Expand All @@ -29,6 +30,7 @@ var slrStateAddresses = map[string]string{
"emr": "module.prod_environment[0].aws_iam_service_linked_role.emr[0]",
"emrserverless": "module.prod_environment[0].aws_iam_service_linked_role.emr_serverless[0]",
"imagebuilder": "module.prod_environment[0].aws_iam_service_linked_role.imagebuilder[0]",
"agentcore": "module.prod_environment[0].aws_iam_service_linked_role.agentcore[0]",
"batch": "module.prod_environment[0].aws_iam_service_linked_role.batch[0]",
}

Expand All @@ -46,6 +48,7 @@ func SLRInState(stateResources []string) *ServiceLinkedRoleStatus {
EMRExists: inState[slrStateAddresses["emr"]],
EMRServerlessExists: inState[slrStateAddresses["emrserverless"]],
ImageBuilderExists: inState[slrStateAddresses["imagebuilder"]],
AgentCoreExists: inState[slrStateAddresses["agentcore"]],
BatchExists: inState[slrStateAddresses["batch"]],
}
}
Expand All @@ -58,6 +61,7 @@ var serviceLinkedRoleChecks = map[string]string{
"emr": "AWSServiceRoleForEMRCleanup",
"emrserverless": "AWSServiceRoleForAmazonEMRServerless",
"imagebuilder": "AWSServiceRoleForImageBuilder",
"agentcore": "AWSServiceRoleForBedrockAgentCoreGatewayNetwork",
"batch": "AWSServiceRoleForBatch",
}

Expand Down Expand Up @@ -98,6 +102,8 @@ func DetectExistingServiceLinkedRoles(profile string) (*ServiceLinkedRoleStatus,
status.EMRServerlessExists = exists
case "imagebuilder":
status.ImageBuilderExists = exists
case "agentcore":
status.AgentCoreExists = exists
case "batch":
status.BatchExists = exists
}
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func runDeploy(cmd *cobra.Command, args []string) error {
CreateEMR: !slrStatus.EMRExists || inState.EMRExists,
CreateEMRServerless: !slrStatus.EMRServerlessExists || inState.EMRServerlessExists,
CreateImageBuilder: !slrStatus.ImageBuilderExists || inState.ImageBuilderExists,
CreateAgentCore: !slrStatus.AgentCoreExists || inState.AgentCoreExists,
CreateBatch: !slrStatus.BatchExists || inState.BatchExists,
}
}
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func runPlan(cmd *cobra.Command, args []string) error {
CreateEMR: !slrStatus.EMRExists || inState.EMRExists,
CreateEMRServerless: !slrStatus.EMRServerlessExists || inState.EMRServerlessExists,
CreateImageBuilder: !slrStatus.ImageBuilderExists || inState.ImageBuilderExists,
CreateAgentCore: !slrStatus.AgentCoreExists || inState.AgentCoreExists,
CreateBatch: !slrStatus.BatchExists || inState.BatchExists,
}
}
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ type ServiceLinkedRoleFlags struct {
CreateEMR bool
CreateEMRServerless bool
CreateImageBuilder bool
CreateAgentCore bool
CreateBatch bool
}

Expand Down Expand Up @@ -618,6 +619,7 @@ func (w *WorkspaceConfig) GenerateTFVars() string {
lines = append(lines, fmt.Sprintf("create_emr_slr = %t", w.SLRFlags.CreateEMR))
lines = append(lines, fmt.Sprintf("create_emr_serverless_slr = %t", w.SLRFlags.CreateEMRServerless))
lines = append(lines, fmt.Sprintf("create_imagebuilder_slr = %t", w.SLRFlags.CreateImageBuilder))
lines = append(lines, fmt.Sprintf("create_agentcore_slr = %t", w.SLRFlags.CreateAgentCore))
lines = append(lines, fmt.Sprintf("create_batch_slr = %t", w.SLRFlags.CreateBatch))
lines = append(lines, "")
}
Expand Down
21 changes: 16 additions & 5 deletions internal/terraform/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ func collectARNs(module *tfModule, modulePrefix string, arns *[]string) {
// Check if this module matches
if strings.HasPrefix(module.Address, modulePrefix) {
for _, resource := range module.Resources {
if arn := extractARN(resource.Values); arn != "" {
if arn := extractARN(resource.Type, resource.Values); arn != "" {
*arns = append(*arns, arn)
}
}
Expand All @@ -464,7 +464,7 @@ func collectAllModuleARNs(module *tfModule, result map[string][]string) {
strings.HasPrefix(moduleName, "tool_testing_") {

for _, resource := range module.Resources {
if arn := extractARN(resource.Values); arn != "" {
if arn := extractARN(resource.Type, resource.Values); arn != "" {
result[moduleName] = append(result[moduleName], arn)
}
}
Expand All @@ -477,11 +477,22 @@ func collectAllModuleARNs(module *tfModule, result map[string][]string) {
}
}

// extractARN extracts an ARN from resource values
func extractARN(values map[string]interface{}) string {
// Try common ARN field names
// extractARN extracts an ARN from resource values. Most AWS resources
// expose an "arn" attribute, but a handful (e.g. aws_cloudformation_stack)
// only expose "id", which is itself the resource's ARN.
func extractARN(resourceType string, values map[string]interface{}) string {
if arn, ok := values["arn"].(string); ok && arn != "" {
return arn
}

idAsARNResourceTypes := map[string]bool{
"aws_cloudformation_stack": true,
}
if idAsARNResourceTypes[resourceType] {
if id, ok := values["id"].(string); ok && strings.HasPrefix(id, "arn:") {
return id
}
}

return ""
}
2 changes: 2 additions & 0 deletions internal/tui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,7 @@ func (m *Model) executeDeploy() tea.Cmd {
CreateEMR: !slrStatus.EMRExists || inState.EMRExists,
CreateEMRServerless: !slrStatus.EMRServerlessExists || inState.EMRServerlessExists,
CreateImageBuilder: !slrStatus.ImageBuilderExists || inState.ImageBuilderExists,
CreateAgentCore: !slrStatus.AgentCoreExists || inState.AgentCoreExists,
CreateBatch: !slrStatus.BatchExists || inState.BatchExists,
}
}
Expand Down Expand Up @@ -1486,6 +1487,7 @@ func (m *Model) executePlan() tea.Cmd {
CreateEMR: !slrStatus.EMRExists || inState.EMRExists,
CreateEMRServerless: !slrStatus.EMRServerlessExists || inState.EMRServerlessExists,
CreateImageBuilder: !slrStatus.ImageBuilderExists || inState.ImageBuilderExists,
CreateAgentCore: !slrStatus.AgentCoreExists || inState.AgentCoreExists,
CreateBatch: !slrStatus.BatchExists || inState.BatchExists,
}
}
Expand Down
87 changes: 87 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,79 @@ module "single_account_privesc_one_hop_to_admin_bedrock_002_bedrockagentcore_sta
flag_value = lookup(local.effective_flags, "bedrock-002-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_bedrock_003_iam_passrole_bedrockagentcore_createagentruntime" {
count = var.enable_single_account_privesc_one_hop_to_admin_bedrock_003_iam_passrole_bedrockagentcore_createagentruntime ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/bedrock-003-iam-passrole+bedrockagentcore-createagentruntime"
providers = {
aws.prod = aws.prod
aws.attacker = aws.attacker
}
account_id = local.prod_account_id
attacker_account_id = local.attacker_account_id
attacker_account_aws_profile = local.effective_attacker_profile
environment = "prod"
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "bedrock-003-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_bedrock_004_bedrockagentcore_invokeagentcommand" {
count = var.enable_single_account_privesc_one_hop_to_admin_bedrock_004_bedrockagentcore_invokeagentcommand ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/bedrock-004-bedrockagentcore-invokeagentcommand"

providers = {
aws.prod = aws.prod
}

account_id = local.prod_account_id
prod_account_aws_profile = var.prod_account_aws_profile
environment = "prod"
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "bedrock-004-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_bedrock_005_iam_passrole_bedrockagentcore_createharness" {
count = var.enable_single_account_privesc_one_hop_to_admin_bedrock_005_iam_passrole_bedrockagentcore_createharness ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/bedrock-005-iam-passrole+bedrockagentcore-createharness"

providers = {
aws.prod = aws.prod
}

account_id = local.prod_account_id
environment = "prod"
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "bedrock-005-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_bedrock_006_iam_passrole_bedrockagentcore_createbrowser" {
count = var.enable_single_account_privesc_one_hop_to_admin_bedrock_006_iam_passrole_bedrockagentcore_createbrowser ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/bedrock-006-iam-passrole+bedrockagentcore-createbrowser"

providers = {
aws.prod = aws.prod
}

account_id = local.prod_account_id
environment = "prod"
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "bedrock-006-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_bedrock_007_bedrockagentcore_startbrowsersession_cdp" {
count = var.enable_single_account_privesc_one_hop_to_admin_bedrock_007_bedrockagentcore_startbrowsersession_cdp ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/bedrock-007-bedrockagentcore-startbrowsersession+cdp"

providers = {
aws.prod = aws.prod
}

account_id = local.prod_account_id
prod_account_aws_profile = var.prod_account_aws_profile
environment = "prod"
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "bedrock-007-to-admin", "flag{MISSING}")
}

module "single_account_privesc_one_hop_to_admin_cognito_identity_001_iam_passrole_cognito_identity_setidentitypoolroles" {
count = var.enable_single_account_privesc_one_hop_to_admin_cognito_identity_001_iam_passrole_cognito_identity_setidentitypoolroles ? 1 : 0
source = "./modules/scenarios/single-account/privesc-one-hop/to-admin/cognito-identity-001-iam-passrole+cognito-identity-setidentitypoolroles"
Expand Down Expand Up @@ -1753,6 +1826,20 @@ module "cross_account_dev_to_prod_one_hop_root_trust_role_assumption" {
flag_value = lookup(local.effective_flags, "root-trust-role-assumption-to-admin", "flag{MISSING}")
}

module "cross_account_dev_to_prod_sts_role_chain_to_admin" {
count = var.enable_cross_account_dev_to_prod_sts_role_chain_to_admin ? 1 : 0
source = "./modules/scenarios/cross-account/dev-to-prod/multi-hop/sts-role-chain"
providers = {
aws.dev = aws.dev
aws.prod = aws.prod
}
dev_account_id = local.dev_account_id
prod_account_id = local.prod_account_id
operations_account_id = local.operations_account_id
resource_suffix = random_string.resource_suffix.result
flag_value = lookup(local.effective_flags, "sts-role-chain-to-admin", "flag{MISSING}")
}

##############################################################################
# CROSS-ACCOUNT OPS-TO-PROD SCENARIOS
##############################################################################
Expand Down
7 changes: 7 additions & 0 deletions modules/environments/prod/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ resource "aws_iam_service_linked_role" "imagebuilder" {
aws_service_name = "imagebuilder.amazonaws.com"
}

# AWSServiceRoleForAmazonBedrockAgentCore — Bedrock AgentCore requires this
# SLR before any CreateAgentRuntime (or CreateHarness / CreateBrowser) call.
# Without it the API returns AccessDeniedException: "Failed creating service
# linked role" even when the caller otherwise has the right permissions.
resource "aws_iam_service_linked_role" "agentcore" {
count = var.create_agentcore_slr ? 1 : 0
aws_service_name = "bedrock-agentcore.amazonaws.com"
# AWSServiceRoleForBatch — AWS Batch requires this SLR to manage compute
# environments on the caller's behalf. It is also implicitly auto-created by
# AWS the first time a Batch compute environment is created, so scenarios
Expand Down
2 changes: 2 additions & 0 deletions modules/environments/prod/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ variable "create_imagebuilder_slr" {
default = true
}

variable "create_agentcore_slr" {
description = "Create the Bedrock AgentCore service-linked role AWSServiceRoleForBedrockAgentCoreGatewayNetwork (set false if it already exists)"
variable "create_batch_slr" {
description = "Create the AWS Batch service-linked role AWSServiceRoleForBatch (set false if it already exists)"
type = bool
Expand Down
Loading