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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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 — 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."