aws-elasticache-snapshot-exfil
elasticache:CopySnapshot copies an existing Redis RDB snapshot to an S3 bucket. If the destination bucket is attacker-accessible, the RDB file - containing all cached in-memory data including application secrets - is exfiltrated. No live cluster access needed.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
Snapshot
?snapshot.provider_type == 'AWS::ElastiCache::Snapshot'
?principal has EFFECTIVE elasticache:CopySnapshot on ?snapshot ARN
emit
| source type | Identity |
|---|---|
| target type | Snapshot |
| source | ?principal |
| target | ?snapshot |
| permissions | elasticache:CopySnapshot |
| conditions | iam_permission scp_or_org_policy resource_policy key_permission |
| state logic | CONDITIONAL(resource_policy): ACTIVE only when the destination S3 bucket is also attacker-accessible (attacker controls the bucket OR a confirming CanWriteData edge to a bucket in the attacker's account exists). Without the S3 write gate, the snapshot is copyable but only to an account-internal destination — emit CONDITIONAL(resource_policy) to flag the capability while noting the destination gate. CONDITIONAL(key_permission) when the source snapshot is encrypted with a CMK: the CopySnapshot call succeeds, but reading the RDB content (S3 object) requires kms:Decrypt on the CMK — the attacker may need to request a cross-account key grant. BLOCKED when an SCP or permission boundary denies elasticache:CopySnapshot. CONDITIONAL(service_state) when the cluster is in a non-snapshotable state (multi-AZ or cluster mode clusters require the cluster to be available for the snapshot to exist and be copyable). |
Narrative
{principal.name} can call elasticache:CopySnapshot to export the Redis snapshot {snapshot.name} to an S3 bucket as an RDB file. The RDB file is a complete serialized dump of all in-memory cache data at snapshot time, including any application secrets (session tokens, API keys, database passwords) stored by the application. An attacker who controls the destination S3 bucket can download and parse the RDB file offline to extract cached data without ever connecting to the live Redis cluster.
Raw rule rules/derived/aws/elasticache.yaml
id: aws-elasticache-snapshot-exfil
emits: CanExfiltrate
description: "elasticache:CopySnapshot copies an existing Redis RDB snapshot to an S3 bucket. If the destination\
\ bucket is attacker-accessible, the RDB file \u2014 containing all cached in-memory data including\
\ application secrets \u2014 is exfiltrated. No live cluster access needed."
match:
- - principal: null
- HasPermission
- snapshot: null
where:
- generic_type(?snapshot) == Snapshot
- ?snapshot.provider_type == 'AWS::ElastiCache::Snapshot'
- ?principal has EFFECTIVE elasticache:CopySnapshot on ?snapshot ARN
emit:
source_type: Identity
target_type: Snapshot
source: ?principal
target: ?snapshot
permissions:
- elasticache:CopySnapshot
conditions:
- iam_permission
- scp_or_org_policy
- resource_policy
- key_permission
state_logic: "CONDITIONAL(resource_policy): ACTIVE only when the destination S3 bucket is also attacker-accessible\
\ (attacker controls the bucket OR a confirming CanWriteData edge to a bucket in the attacker's account\
\ exists). Without the S3 write gate, the snapshot is copyable but only to an account-internal destination\
\ \u2014 emit CONDITIONAL(resource_policy) to flag the capability while noting the destination gate.\
\ CONDITIONAL(key_permission) when the source snapshot is encrypted with a CMK: the CopySnapshot call\
\ succeeds, but reading the RDB content (S3 object) requires kms:Decrypt on the CMK \u2014 the attacker\
\ may need to request a cross-account key grant. BLOCKED when an SCP or permission boundary denies\
\ elasticache:CopySnapshot. CONDITIONAL(service_state) when the cluster is in a non-snapshotable state\
\ (multi-AZ or cluster mode clusters require the cluster to be available for the snapshot to exist\
\ and be copyable)."
confidence: min(contributing_confidences) * 0.80
derived_from:
- ?principal HasPermission ?snapshot (elasticache:CopySnapshot effective permission)
false_positive_note: "The S3 destination is the key gate. elasticache:CopySnapshot's DestinationS3Location\
\ parameter specifies the bucket and prefix. The attacker must have a bucket that accepts the write\
\ (bucket policy must allow s3:PutObject from the ElastiCache service principal or the caller's account).\
\ Do NOT emit ACTIVE unless the destination bucket ownership is confirmed as attacker-accessible.\
\ For encrypted snapshots (AtRestEncryptionEnabled with a CMK), the RDB data in S3 is KMS-encrypted;\
\ the attacker additionally needs kms:Decrypt on the CMK or a cross-account key grant \u2014 add key_permission\
\ condition for encrypted clusters. AWS-managed (default SSE-S3) snapshot encryption does NOT require\
\ a key grant. This rule requires an existing snapshot \u2014 the elasticache:CreateSnapshot permission\
\ is a prerequisite (or a snapshot is already scheduled/available); it is not modeled here. NOTE:\
\ This rule is conservative by design (Phase-6 low-relevance guidance for ElastiCache: 1-3 rules).\
\ The exfil path is real but requires two permissions (snapshot + S3 write) and is more complex than\
\ the AUTH-token-reset path. Cached data in ElastiCache is often transient; impact depends on what\
\ the application stores (session tokens expire, API keys may still be valid)."
narrative: '{principal.name} can call elasticache:CopySnapshot to export the Redis snapshot {snapshot.name}
to an S3 bucket as an RDB file. The RDB file is a complete serialized dump of all in-memory cache
data at snapshot time, including any application secrets (session tokens, API keys, database passwords)
stored by the application. An attacker who controls the destination S3 bucket can download and parse
the RDB file offline to extract cached data without ever connecting to the live Redis cluster.'