federate-then-enter-account
Federating into a Role homed in an AWS account gives the federated principal a foothold in that account.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?internal) in [
Role, ServiceAccount, MachineIdentity]
node_class(?p) in [Identity] # FederatedIdentity / WorkloadIdentity are Identity subtypes
node_type(home_boundary(?internal)) == Account # AWS
home_boundary(?internal) is a boundary the federated principal is not already in
emit
| source type | Identity |
|---|---|
| target type | Account |
| source | ?p |
| target | home_boundary(?internal) (Account) |
| conditions | trust_condition |
| state logic | inherit from the CanFederateAs input: ACTIVE only when the federation is ACTIVE (broad/unpinned trust the attacker satisfies); CONDITIONAL(trust_condition) when the federation is CONDITIONAL (pinned trust); BLOCKED if the federation is BLOCKED. The foothold is exactly as strong as the takeover that produces it. |
Narrative
{p.name} can federate in as {internal.name}, which is homed in account {boundary.name}; the federation therefore lands the attacker inside {boundary.name}, operating as {internal.name}.
Raw rule rules/derived/federation-chains.yaml
id: federate-then-enter-account
emits: CanEnterAccount
description: Federating into a Role homed in an AWS account gives the federated principal a foothold in
that account.
applies_to:
- '*'
match:
- - p: null
- CanFederateAs
- internal: null
where:
- node_type(?internal) in [Role, ServiceAccount, MachineIdentity]
- 'node_class(?p) in [Identity] # FederatedIdentity / WorkloadIdentity are Identity subtypes'
- 'node_type(home_boundary(?internal)) == Account # AWS'
- home_boundary(?internal) is a boundary the federated principal is not already in
emit:
source_type: Identity
target_type: Account
source: ?p
target: home_boundary(?internal) (Account)
permissions: []
conditions:
- trust_condition
state_logic: 'inherit from the CanFederateAs input: ACTIVE only when the federation is ACTIVE (broad/unpinned
trust the attacker satisfies); CONDITIONAL(trust_condition) when the federation is CONDITIONAL (pinned
trust); BLOCKED if the federation is BLOCKED. The foothold is exactly as strong as the takeover that
produces it.'
confidence: min(contributing_confidences)
derived_from:
- ?p CanFederateAs ?internal
- home_boundary(?internal) == Account
false_positive_note: "The foothold is entry AS ?internal \u2014 it grants the federated principal whatever\
\ ?internal itself can do inside the account, NOT the account's full privilege (rule 1's trust breadth\
\ decides reachability; boundary control comes only if ?internal is itself an admin there \u2014 see\
\ hierarchy-chains enter-boundary-rollups for the Controls upgrade condition). Do NOT emit for a federation\
\ whose target is homed in the principal's OWN account (no boundary crossed). AWS cross-account role\
\ ASSUME (not federation) is already handled by can-control's enter-account-via-assume \u2014 do not\
\ double-emit CanEnterAccount for that case; this rule covers the CanFederateAs (web-identity / RolesAnywhere)\
\ path. Respect the trust_condition gate \u2014 never upgrade a CONDITIONAL federation to an ACTIVE\
\ foothold."
narrative: '{p.name} can federate in as {internal.name}, which is homed in account {boundary.name};
the federation therefore lands the attacker inside {boundary.name}, operating as {internal.name}.'