Attack path provider logo

Identity Center Permission Set Escalation (SSO-001)

aws-sso-privesc-permission-set-escalation

Privilege escalationgeneralSSO-001
by Prowler

Attack path

IAM principal
Required actions
sso:CreatePermissionSetsso:AttachManagedPolicyToPermissionSetsso:CreateAccountAssignment
Run code on AWS resources
Steal role credentials
Privilege escalation

Schematic attack path derived from the query definition. See the full write-up via the attribution link below.

Required IAM permissions

sso:CreatePermissionSetsso:AttachManagedPolicyToPermissionSetsso:CreateAccountAssignment

Summary

Create an administrative Identity Center permission set and assign it to gain organization-wide admin access.

Description

Detect principals that hold sso:CreatePermissionSet, sso:AttachManagedPolicyToPermissionSet, and sso:CreateAccountAssignment together. With all three, a principal can create a new IAM Identity Center permission set, attach the AdministratorAccess managed policy to it, and assign it to their own user or group for any account in the organization, gaining administrative access across the organization through the Identity Center portal.

openCypher query

// Find principals with sso:CreatePermissionSet permission
        MATCH path_principal = (aws:AWSAccount {id: $provider_uid})--(principal:AWSPrincipal)-[:POLICY]->(policy:AWSPolicy)-[:STATEMENT]->(stmt:AWSPolicyStatement {effect: 'Allow'})
        MATCH (stmt)-[:HAS_ACTION]->(act:AWSPolicyStatementActionItem)
        WHERE toLower(act.value) IN ['sso:*', 'sso:createpermissionset']
            OR act.value = '*'
        MATCH (stmt)-[:HAS_RESOURCE]->(res:AWSPolicyStatementResourceItem)
        WHERE res.value = '*'
        WITH DISTINCT aws, principal, path_principal

        // Find sso:AttachManagedPolicyToPermissionSet permission on the same principal
        MATCH (principal)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(stmt2:AWSPolicyStatement {effect: 'Allow'})-[:HAS_ACTION]->(act2:AWSPolicyStatementActionItem)
        WHERE toLower(act2.value) IN ['sso:*', 'sso:attachmanagedpolicytopermissionset']
            OR act2.value = '*'
        MATCH (stmt2)-[:HAS_RESOURCE]->(res2:AWSPolicyStatementResourceItem)
        WHERE res2.value = '*'
        WITH DISTINCT principal, path_principal

        // Find sso:CreateAccountAssignment permission on the same principal
        MATCH (principal)-[:POLICY]->(:AWSPolicy)-[:STATEMENT]->(stmt3:AWSPolicyStatement {effect: 'Allow'})-[:HAS_ACTION]->(act3:AWSPolicyStatementActionItem)
        WHERE toLower(act3.value) IN ['sso:*', 'sso:createaccountassignment']
            OR act3.value = '*'
        MATCH (stmt3)-[:HAS_RESOURCE]->(res3:AWSPolicyStatementResourceItem)
        WHERE res3.value = '*'

        WITH DISTINCT path_principal
        WITH collect(path_principal) AS paths
        UNWIND paths AS p
        UNWIND nodes(p) AS n

        WITH paths, collect(DISTINCT n) AS unique_nodes
        UNWIND unique_nodes AS n

        OPTIONAL MATCH (n)-[pfr:HAS_FINDING]-(pf:ProwlerFinding {status: 'FAIL'})

        RETURN paths, collect(DISTINCT pf) as dpf, collect(DISTINCT pfr) as dpfr

Full attack path write-up

pathfinding.cloud - SSO-001 - sso:CreatePermissionSet + sso:AttachManagedPolicyToPermissionSet + sso:CreateAccountAssignment