aws-codedeploy-hook-exposes-instance-role-cred

CodeDeploy lifecycle hook scripts (running as root on EC2/on-prem targets) can read the instance profile role credentials from IMDS, exposing them to the attacker's code.

derived aws emits ExposesCredential

match

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

{'deploygroup': None} CanExecuteCommand {'vm': None} {'vm': None} ExecutesAs {'instancerole': None}

where

node_type(?deploygroup) == AutomationService ?deploygroup.provider_type == AWS::CodeDeploy::DeploymentGroup ?deploygroup.compute_platform in [Server, OnPremise] node_type(?vm) == VirtualMachine ?instancerole is the EC2 instance profile role (MachineIdentity/Role) of ?vm

emit

source typeCompute
target typeRole
source?vm
target?instancerole
conditionsservice_state
state logicACTIVE when compute_platform is confirmed as Server/OnPremise AND the target instance ?vm has an instance profile (instancerole is non-null). CONDITIONAL (service_state) when instance profile attachment is unconfirmed. Not applicable for ECS or Lambda compute platforms. The source is the VirtualMachine (Compute subtype), which is schema-conformant for ExposesCredential.

Narrative

A CodeDeploy lifecycle hook script running as root on target instance {vm.name} can query the IMDS endpoint and obtain credentials for the instance profile role {instancerole.name}.

Raw rule rules/derived/aws/codedeploy.yaml

id: aws-codedeploy-hook-exposes-instance-role-cred
emits: ExposesCredential
description: CodeDeploy lifecycle hook scripts (running as root on EC2/on-prem targets) can read the instance
  profile role credentials from IMDS, exposing them to the attacker's code.
match:
- - deploygroup: null
  - CanExecuteCommand
  - vm: null
- - vm: null
  - ExecutesAs
  - instancerole: null
where:
- node_type(?deploygroup) == AutomationService
- ?deploygroup.provider_type == AWS::CodeDeploy::DeploymentGroup
- ?deploygroup.compute_platform in [Server, OnPremise]
- node_type(?vm) == VirtualMachine
- ?instancerole is the EC2 instance profile role (MachineIdentity/Role) of ?vm
emit:
  source_type: Compute
  target_type: Role
  source: ?vm
  target: ?instancerole
  permissions: []
  conditions:
  - service_state
  state_logic: ACTIVE when compute_platform is confirmed as Server/OnPremise AND the target instance ?vm
    has an instance profile (instancerole is non-null). CONDITIONAL (service_state) when instance profile
    attachment is unconfirmed. Not applicable for ECS or Lambda compute platforms. The source is the VirtualMachine
    (Compute subtype), which is schema-conformant for ExposesCredential.
  confidence: 0.9
  derived_from:
  - ?deploygroup CanExecuteCommand ?vm
  - ?vm ExecutesAs ?instancerole
  false_positive_note: IMDSv2 with a hop-limit of 1 blocks IMDS reads from inside containers but NOT from
    a root shell running directly on the instance (hop-limit 1 allows direct local access). A hook script
    running as root on the instance CAN query IMDSv2 even with hop-limit 1; the IMDSv2 hop-limit only
    blocks nested requests (container-inside-container or cross-hop). Do not treat IMDSv2 alone as a blocker
    for hook-script credential theft. BLOCKED only if the instance metadata service is explicitly disabled
    (HttpEndpoint=disabled on the instance metadata options). The source must be the VirtualMachine, not
    the deployment group, because ExposesCredential.source is restricted to [Compute, Storage, Data, Messaging,
    Secret].
  narrative: A CodeDeploy lifecycle hook script running as root on target instance {vm.name} can query
    the IMDS endpoint and obtain credentials for the instance profile role {instancerole.name}.
move · open · esc close