aws-ebs-copy-snapshot-exfil

ec2:CopySnapshot called from an attacker account (on a shared snapshot) or with a destination in another account copies snapshot data cross-boundary.

derived aws emits CanExfiltrate

match

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

{'principal': None} CanRead {'snapshot': None}

where

node_type(?snapshot) == Snapshot ?principal has EFFECTIVE ec2:CopySnapshot ?snapshot.owner_account != ?principal.account (principal reads snapshot from a different account than the owner)

emit

source typeIdentity
target typeSnapshot
source?principal
target?snapshot
permissionsec2:CopySnapshot
conditionsiam_permission key_permission
state logicACTIVE when ec2:CopySnapshot is effective AND the snapshot is readable from the caller's account (createVolumePermission grants the caller or 'all'). The WHERE clause ensures cross-account access: the snapshot belongs to a different account than the principal. CONDITIONAL(key_permission) if the snapshot is CMK-encrypted and the key is not shared with the destination account. NOTE: The destination account is a runtime decision (caller specifies it in the CopySnapshot call) and cannot be determined from the graph at rule-match time — only verify that the source is cross-account. Analysts must review the actual CopySnapshot calls to confirm exfiltration to attacker accounts.

Narrative

{principal.name} can call ec2:CopySnapshot to duplicate {snapshot.name} (owned by a different account) to a caller-specified destination, gaining a persistent copy of the disk data outside the original account boundary.

Raw rule rules/derived/aws/ebs.yaml

id: aws-ebs-copy-snapshot-exfil
emits: CanExfiltrate
description: ec2:CopySnapshot called from an attacker account (on a shared snapshot) or with a destination
  in another account copies snapshot data cross-boundary.
match:
- - principal: null
  - CanRead
  - snapshot: null
where:
- node_type(?snapshot) == Snapshot
- ?principal has EFFECTIVE ec2:CopySnapshot
- ?snapshot.owner_account != ?principal.account (principal reads snapshot from a different account than
  the owner)
emit:
  source_type: Identity
  target_type: Snapshot
  source: ?principal
  target: ?snapshot
  permissions:
  - ec2:CopySnapshot
  conditions:
  - iam_permission
  - key_permission
  state_logic: "ACTIVE when ec2:CopySnapshot is effective AND the snapshot is readable from the caller's\
    \ account (createVolumePermission grants the caller or 'all'). The WHERE clause ensures cross-account\
    \ access: the snapshot belongs to a different account than the principal. CONDITIONAL(key_permission)\
    \ if the snapshot is CMK-encrypted and the key is not shared with the destination account. NOTE: The\
    \ destination account is a runtime decision (caller specifies it in the CopySnapshot call) and cannot\
    \ be determined from the graph at rule-match time \u2014 only verify that the source is cross-account.\
    \ Analysts must review the actual CopySnapshot calls to confirm exfiltration to attacker accounts."
  confidence: 0.85
  derived_from:
  - <CanRead edge on ?snapshot>
  false_positive_note: "CopySnapshot copies into the caller's account (or a caller-specified destination\
    \ if using the Destination parameter) \u2014 the caller must already have createVolumePermission on\
    \ the source snapshot (own it or have it shared). Do not emit if the snapshot is only accessible within\
    \ the owner account. The destination account and region are API call parameters, not graph properties\
    \ \u2014 this rule conservatively emits when cross-account read is confirmed, but analysts must validate\
    \ the actual call destinations. Cross-region copy requires ec2:CopySnapshot to be permitted in the\
    \ destination region as well (honor region-level SCPs). For CMK-encrypted snapshots, the destination\
    \ account must either have the key shared or be able to decrypt under the source account's key access\
    \ \u2014 verify this before reporting ACTIVE."
  narrative: '{principal.name} can call ec2:CopySnapshot to duplicate {snapshot.name} (owned by a different
    account) to a caller-specified destination, gaining a persistent copy of the disk data outside the
    original account boundary.'
move · open · esc close