aws-eks-node-imds-credential

Pods on EC2 managed nodes with IMDS hop-limit >= 2 can reach 169.254.169.254 and retrieve temporary credentials for the node group IAM role. Any code execution inside such a pod inherits the node role without any IRSA or Pod Identity annotation.

derived aws emits ExposesCredential

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'node': None} ExecutesAs {'node_role': None}

where

node_type(?node) == VirtualMachine # EC2 node in EKS managed node group node_type(?node_role) in [MachineIdentity, Role] ?node.metadataOptions.httpPutResponseHopLimit >= 2 # IMDS reachable by pods pods are co-located on ?node (EKS managed node group, not Fargate)

emit

source typeCompute
target typeCredential
source?node
target?node_role
conditionsnetwork_reachability service_state
state logicCONDITIONAL(network_reachability): ACTIVE only when the hop-limit is confirmed >= 2 (from ec2:DescribeLaunchTemplates metadataOptions.httpPutResponseHopLimit; default for EKS managed nodes is 2). POTENTIAL when hop-limit is uncollected (assume exploitable until confirmed hardened). BLOCKED when hop-limit = 1 (recommended hardening — pods cannot reach IMDS). NEVER emit for Fargate pods (no host EC2, no IMDS).

Narrative

EC2 node {node.name} (EKS managed node group, IMDS hop-limit >= 2) runs as IAM role {node_role.name}. Any pod co-located on this node can reach the instance metadata endpoint (169.254.169.254) and retrieve temporary STS credentials for {node_role.name} without any IRSA or Pod Identity annotation - the node role's temporary credentials are ambient and available to all pod code on this node.

Raw rule rules/derived/aws/eks.yaml

id: aws-eks-node-imds-credential
emits: ExposesCredential
description: Pods on EC2 managed nodes with IMDS hop-limit >= 2 can reach 169.254.169.254 and retrieve
  temporary credentials for the node group IAM role. Any code execution inside such a pod inherits the
  node role without any IRSA or Pod Identity annotation.
match:
- - node: null
  - ExecutesAs
  - node_role: null
where:
- 'node_type(?node) == VirtualMachine  # EC2 node in EKS managed node group'
- node_type(?node_role) in [MachineIdentity, Role]
- '?node.metadataOptions.httpPutResponseHopLimit >= 2  # IMDS reachable by pods'
- pods are co-located on ?node (EKS managed node group, not Fargate)
emit:
  source_type: Compute
  target_type: Credential
  source: ?node
  target: ?node_role
  permissions: []
  conditions:
  - network_reachability
  - service_state
  state_logic: "CONDITIONAL(network_reachability): ACTIVE only when the hop-limit is confirmed >= 2 (from\
    \ ec2:DescribeLaunchTemplates metadataOptions.httpPutResponseHopLimit; default for EKS managed nodes\
    \ is 2). POTENTIAL when hop-limit is uncollected (assume exploitable until confirmed hardened). BLOCKED\
    \ when hop-limit = 1 (recommended hardening \u2014 pods cannot reach IMDS). NEVER emit for Fargate\
    \ pods (no host EC2, no IMDS)."
  confidence: 0.8
  derived_from:
  - ?node ExecutesAs ?node_role
  - ?node.metadataOptions.httpPutResponseHopLimit >= 2 (node IMDS reachable)
  false_positive_note: "NEVER emit for Fargate pods \u2014 Fargate has no host EC2 instance and no IMDS\
    \ exposure. Only for EC2 launch type (managed node groups). Downgrade to POTENTIAL when hop-limit\
    \ is not collected (unknown); BLOCKED when hop-limit = 1. The node role's temporary STS credentials\
    \ are retrievable by ANY pod on the node, not just pods with specific RBAC \u2014 it is an ambient\
    \ credential available to all co-located pods. This is a fact edge (the node exposes the temporary\
    \ credential to co-located pods); the path-finding value comes from attacker code running in any such\
    \ pod."
  narrative: "EC2 node {node.name} (EKS managed node group, IMDS hop-limit >= 2) runs as IAM role {node_role.name}.\
    \ Any pod co-located on this node can reach the instance metadata endpoint (169.254.169.254) and retrieve\
    \ temporary STS credentials for {node_role.name} without any IRSA or Pod Identity annotation \u2014\
    \ the node role's temporary credentials are ambient and available to all pod code on this node."
move · open · esc close