poisoned-image-executes-as

A workload poisoned via a registry image push runs as its identity, so the pusher executes as that identity.

derived any cloud emits CanExecuteAs

match

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

{'principal': None} CanModifyCode {'consumer': None} {'consumer': None} ExecutesAs {'identity': None}

where

node_type(?consumer) in [Compute, ContainerService, ContainerTask, KubernetesWorkload, Container, VirtualMachine, ServerlessFunction, BuildWorker] node_class(?identity) == Identity

emit

source typeIdentity
target typeIdentity
source?principal
target?identity
conditionsimage_pulled
state logicinherit weakest from the contributing CanModifyCode (rule 1a) and ExecutesAs: ACTIVE only when the poison actually reaches the running workload (rule 1a ACTIVE — mutable tag re-pulled); CONDITIONAL(image_pulled) when the pull/ redeploy is not yet established; BLOCKED when the consumer is digest-pinned so rule 1a did not fire ACTIVE. Never stronger than the CanModifyCode input.

Narrative

{principal.name} controls the code {consumer.name} runs (via a poisoned registry image), and {consumer.name} executes as {identity.name}; running the poisoned image therefore executes attacker code as {identity.name}.

Raw rule rules/derived/container-chains.yaml

id: poisoned-image-executes-as
emits: CanExecuteAs
description: A workload poisoned via a registry image push runs as its identity, so the pusher executes
  as that identity.
applies_to:
- '*'
match:
- - principal: null
  - CanModifyCode
  - consumer: null
- - consumer: null
  - ExecutesAs
  - identity: null
where:
- node_type(?consumer) in [Compute, ContainerService, ContainerTask, KubernetesWorkload, Container, VirtualMachine,
  ServerlessFunction, BuildWorker]
- node_class(?identity) == Identity
emit:
  source_type: Identity
  target_type: Identity
  source: ?principal
  target: ?identity
  permissions: []
  conditions:
  - image_pulled
  state_logic: "inherit weakest from the contributing CanModifyCode (rule 1a) and ExecutesAs: ACTIVE only\
    \ when the poison actually reaches the running workload (rule 1a ACTIVE \u2014 mutable tag re-pulled);\
    \ CONDITIONAL(image_pulled) when the pull/ redeploy is not yet established; BLOCKED when the consumer\
    \ is digest-pinned so rule 1a did not fire ACTIVE. Never stronger than the CanModifyCode input."
  confidence: min(contributing_confidences)
  derived_from:
  - ?principal CanModifyCode ?consumer
  - ?consumer ExecutesAs ?identity
  false_positive_note: "This dedupes with can-execute-as execute-as-via-code-modify on (CanExecuteAs,\
    \ ?principal, ?identity) \u2014 SAME collapse, container/supply-chain framing; emit once, do not double-count\
    \ weight where both fire. The escalation consequence (identity more privileged than the principal)\
    \ is produced by can-execute-as / can-control off this CanExecuteAs \u2014 cite, do not duplicate.\
    \ Carries rule 1a's image_pulled gate: a digest-pinned or never-re-pulled consumer does not yield\
    \ an ACTIVE execute-as. Only emit for ?consumer whose ExecutesAs is a real, mounted execution identity\
    \ (task role / pod SA / WI), not a placeholder."
  narrative: '{principal.name} controls the code {consumer.name} runs (via a poisoned registry image),
    and {consumer.name} executes as {identity.name}; running the poisoned image therefore executes attacker
    code as {identity.name}.'
move · open · esc close