Skip to content

Handle target-dependent privilege-escalation paths (sts:AssumeRole, ssm, CodeBuild/CloudFormation/AppRunner update) — needs reachability graph, not action matching #585

@kmcquade

Description

@kmcquade

Background

PR #584 added detection for 37 of the 46 pathfinding.cloud privilege-escalation paths cloudsplaining was missing. 7 were deliberately left out — not as an oversight, but because they're a structural mismatch for cloudsplaining's analysis model. This issue tracks how to handle them properly.

The problem

cloudsplaining flags a policy when its expanded actions are a superset of a known method's action list (PRIVILEGE_ESCALATION_METHODS in cloudsplaining/shared/constants.py, matched in scan/policy_document.py). That works for self-contained escalations — e.g. iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction, where the policy itself proves the escalation.

A class of pathfinding.cloud paths are instead target-dependent: the action is privilege escalation only if an existing resource already has a more-privileged role attached. cloudsplaining only sees the policy in front of it — it has no view of which roles exist, what they can do, or what's attached to which resource — so it cannot distinguish escalation from completely benign use. Flagging these on an action match alone would produce massive false positives.

The 7 held paths

pathfinding path trigger action privilege escalation only if…
sts-001 sts:AssumeRole the assumed role is more privileged than the caller
ssm-001 ssm:StartSession the target EC2 instance has a privileged instance role
ssm-002 ssm:SendCommand the target EC2 instance has a privileged instance role
cloudformation-002 cloudformation:UpdateStack the existing stack has an admin service role
apprunner-002 apprunner:UpdateService the existing service has a privileged role
codebuild-002 codebuild:StartBuild the existing project has a privileged role (+ buildspec-override)
codebuild-003 codebuild:StartBuildBatch the existing project has a privileged role (+ buildspec-override)

Note: sts:AssumeRole is already surfaced today under Credentials Exposure (ACTIONS_THAT_RETURN_CREDENTIALS) — it's just not (and shouldn't be) labeled as guaranteed privilege escalation. Adding it as a standalone privesc method would also undo the precision intent of #581.

Why it's hard

cloudsplaining is fundamentally a single-policy, action-level analyzer. Deciding whether sts:AssumeRole / ssm:SendCommand / cloudformation:UpdateStack is actually privesc requires cross-resource reachability:

principal → action → target resource → attached role → that role's effective permissions

That's a graph / relationship problem, not a subset match.

Proposed direction (future — not now)

  • Build a privilege-escalation reachability graph over the account authorization details (principals, roles, trust policies, resource→role attachments) so we can evaluate whether a target is genuinely more privileged before flagging.
  • Interim option: surface these as a separate, lower-severity / informational signal ("potential privilege escalation if the target resource has a privileged role") kept clearly distinct from the High-severity PrivilegeEscalation findings, so it doesn't drown out the precise detections.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions