aws-backup-cross-account-exfil

backup:StartCopyJob copies individual recovery points from a backup vault to a vault in a different AWS account, enabling complete data exfiltration by restoring the copied backups in the attacker's account.

derived aws emits CanExfiltrate

match

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

{'principal': None} HasPermission {'vault': None}

where

node_type(?vault) == Backup ?vault.provider_type == 'AWS::Backup::BackupVault' ?principal has EFFECTIVE backup:StartCopyJob on ?vault ARN (or on contained recovery points) ?recovery_point node_type == Backup AND ?recovery_point.provider_type == 'AWS::Backup::RecoveryPoint' AND ?recovery_point ContainsResourceReference ?vault A destination vault ARN in another account ID is available (from backup plan rules[].copyActions[].destinationVaultArn OR caller-supplied at runtime) destination vault's access policy permits backup:CopyIntoBackupVault from source account (or Principal: '*') ?vault is NOT a Logically Air-Gapped (LAG) vault (LAG vaults block cross-account copy by design)

emit

source typeIdentity
target typeStorage
source?principal
target?recovery_point
permissionsbackup:StartCopyJob
conditionsiam_permission resource_policy scp_or_org_policy key_permission
state logicACTIVE when: (1) backup:StartCopyJob is confirmed EFFECTIVE on ?vault (identity policy + no SCP deny), (2) the destination vault's access policy is confirmed to permit backup:CopyIntoBackupVault from the source account or a wildcard principal, AND (3) the recovery point's KMS encryption key (if CMK) is accessible in the destination account (or re-encryption is configured). CONDITIONAL(resource_policy) when the destination vault policy hasn't been evaluated or may restrict cross-account copy-in. CONDITIONAL(key_permission) when recovery points are encrypted with a CMK and the destination account's access to that key is unresolved — the copy will fail or produce an unusable backup without kms:Decrypt on the source key. CONDITIONAL(scp_or_org_policy) when an SCP may block backup:StartCopyJob cross-account. BLOCKED when: an SCP explicitly denies backup:StartCopyJob; OR the vault is a LAG vault (cross-account copy blocked by design); OR Vault Lock is active in compliance mode AFTER the cool-off period (within the MinRetentionDays window). NOTE: Vault Lock blocks backup:DeleteBackupVaultAccessPolicy but NOT backup:StartCopyJob — the copy capability remains available even in locked compliance vaults. Model locked vaults as a defensive measure (prevents deletion/policy change), not as a gate on copy-out. Same-account cross-region copies: downgrade to POTENTIAL — no account boundary crossed; not emitted as CanExfiltrate.

Narrative

{principal.name} can copy recovery points (individual backups) from vault {vault.name} (backup:StartCopyJob) to a vault in an external AWS account. The external account can then restore the copied backups to get full replicas of the source resources - databases, EBS volumes, or filesystems - and read all data.

Raw rule rules/derived/aws/backup.yaml

id: aws-backup-cross-account-exfil
emits: CanExfiltrate
description: backup:StartCopyJob copies individual recovery points from a backup vault to a vault in a
  different AWS account, enabling complete data exfiltration by restoring the copied backups in the attacker's
  account.
match:
- - principal: null
  - HasPermission
  - vault: null
where:
- node_type(?vault) == Backup
- ?vault.provider_type == 'AWS::Backup::BackupVault'
- ?principal has EFFECTIVE backup:StartCopyJob on ?vault ARN (or on contained recovery points)
- ?recovery_point node_type == Backup AND ?recovery_point.provider_type == 'AWS::Backup::RecoveryPoint'
  AND ?recovery_point ContainsResourceReference ?vault
- A destination vault ARN in another account ID is available (from backup plan rules[].copyActions[].destinationVaultArn
  OR caller-supplied at runtime)
- 'destination vault''s access policy permits backup:CopyIntoBackupVault from source account (or Principal:
  ''*'')'
- ?vault is NOT a Logically Air-Gapped (LAG) vault (LAG vaults block cross-account copy by design)
emit:
  source_type: Identity
  target_type: Storage
  source: ?principal
  target: ?recovery_point
  permissions:
  - backup:StartCopyJob
  conditions:
  - iam_permission
  - resource_policy
  - scp_or_org_policy
  - key_permission
  state_logic: "ACTIVE when: (1) backup:StartCopyJob is confirmed EFFECTIVE on ?vault (identity policy\
    \ + no SCP deny), (2) the destination vault's access policy is confirmed to permit backup:CopyIntoBackupVault\
    \ from the source account or a wildcard principal, AND (3) the recovery point's KMS encryption key\
    \ (if CMK) is accessible in the destination account (or re-encryption is configured). CONDITIONAL(resource_policy)\
    \ when the destination vault policy hasn't been evaluated or may restrict cross-account copy-in. CONDITIONAL(key_permission)\
    \ when recovery points are encrypted with a CMK and the destination account's access to that key is\
    \ unresolved \u2014 the copy will fail or produce an unusable backup without kms:Decrypt on the source\
    \ key. CONDITIONAL(scp_or_org_policy) when an SCP may block backup:StartCopyJob cross-account. BLOCKED\
    \ when: an SCP explicitly denies backup:StartCopyJob; OR the vault is a LAG vault (cross-account copy\
    \ blocked by design); OR Vault Lock is active in compliance mode AFTER the cool-off period (within\
    \ the MinRetentionDays window). NOTE: Vault Lock blocks backup:DeleteBackupVaultAccessPolicy but NOT\
    \ backup:StartCopyJob \u2014 the copy capability remains available even in locked compliance vaults.\
    \ Model locked vaults as a defensive measure (prevents deletion/policy change), not as a gate on copy-out.\
    \ Same-account cross-region copies: downgrade to POTENTIAL \u2014 no account boundary crossed; not\
    \ emitted as CanExfiltrate."
  confidence: min(contributing_confidences) * 0.90
  derived_from:
  - ?principal HasPermission ?vault (backup:StartCopyJob effective permission)
  - ?recovery_point ContainsResourceReference ?vault (recovery point resides in vault)
  - backup plan rules[].copyActions[].destinationVaultArn shows cross-account vault (GetBackupPlan evidence)
  - destination vault GetBackupVaultAccessPolicy confirms backup:CopyIntoBackupVault grant
  false_positive_note: "Two independent conditions must hold: (1) backup:StartCopyJob is EFFECTIVE for\
    \ the caller on the source vault; (2) the DESTINATION vault's resource policy permits backup:CopyIntoBackupVault\
    \ from the source account \u2014 without this second condition the copy job is rejected by the destination\
    \ account. Do NOT emit ACTIVE without evidence of the destination policy grant. Same-account cross-region\
    \ copies are NOT exfiltration across an account boundary \u2014 do not emit CanExfiltrate; they may\
    \ warrant CanReadData (rule 2). For LAG vaults, cross-account copy is blocked by the vault type itself\
    \ \u2014 emit BLOCKED. KMS-encrypted recovery points (CMK): the copy will only succeed if the destination\
    \ is granted kms:Decrypt / kms:ReEncrypt on the source key, or re-encryption to a destination key\
    \ is configured \u2014 emit CONDITIONAL(key_permission) when unresolved. Vault Lock in compliance\
    \ mode: once active (after cool-off period), Vault Lock blocks backup:DeleteBackupVaultAccessPolicy\
    \ but DOES NOT block backup:StartCopyJob. A locked vault is defensive (prevents deletion), but cross-account\
    \ copy is not gated by the lock. Emit ACTIVE for copy-out even when Vault Lock is active compliance-locked."
  narrative: "{principal.name} can copy recovery points (individual backups) from vault {vault.name} (backup:StartCopyJob)\
    \ to a vault in an external AWS account. The external account can then restore the copied backups\
    \ to get full replicas of the source resources \u2014 databases, EBS volumes, or filesystems \u2014\
    \ and read all data."
move · open · esc close