aws-elasticache-create-and-exfil
elasticache:CreateSnapshot combined with elasticache:CopySnapshot allows creating a fresh Redis RDB snapshot from a running cache and immediately exporting it to an attacker-controlled S3 bucket. The RDB file is a complete serialized dump of all in-memory data at snapshot time, bypassing the need for scheduled snapshots. No live cluster access needed.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
Cache
?cache.provider_type in ['AWS::ElastiCache::ReplicationGroup', 'AWS::ElastiCache::ServerlessCache']
?principal has EFFECTIVE elasticache:CreateSnapshot on ?cache ARN AND ?principal has EFFECTIVE elasticache:CopySnapshot on snapshots created from ?cache
?cache.properties.Engine in ['redis', 'valkey'] (Memcached does not support snapshots; only Redis/Valkey)
emit
| source type | Identity |
|---|---|
| target type | Cache |
| source | ?principal |
| target | ?cache |
| permissions | elasticache:CreateSnapshot elasticache:CopySnapshot |
| conditions | iam_permission scp_or_org_policy resource_policy service_state |
| state logic | CONDITIONAL(resource_policy): ACTIVE only when BOTH CreateSnapshot and CopySnapshot are confirmed EFFECTIVE on the cache ARN, and the destination S3 bucket is 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 creatable but only exportable to an account-internal destination — emit CONDITIONAL(resource_policy) to flag the capability while noting the destination gate. CONDITIONAL(iam_permission) when either permission is unresolved. BLOCKED when an SCP or permission boundary denies either CreateSnapshot or CopySnapshot. CONDITIONAL(service_state) when the cache is in a non-snapshotable state (creating, modifying, deleting). |
Narrative
{principal.name} can call elasticache:CreateSnapshot to create a fresh Redis snapshot from {cache.name}, then elasticache:CopySnapshot to export the resulting RDB file to an S3 bucket. If the destination S3 bucket is attacker-controlled, the full serialized dump of all in-memory cache data - including any application secrets (session tokens, API keys, database passwords) stored by the application - is exfiltrated without ever connecting to the live cluster or waiting for a scheduled backup.
Raw rule rules/derived/aws/elasticache.yaml
id: aws-elasticache-create-and-exfil
emits: CanExfiltrate
description: elasticache:CreateSnapshot combined with elasticache:CopySnapshot allows creating a fresh
Redis RDB snapshot from a running cache and immediately exporting it to an attacker-controlled S3 bucket.
The RDB file is a complete serialized dump of all in-memory data at snapshot time, bypassing the need
for scheduled snapshots. No live cluster access needed.
match:
- - principal: null
- HasPermission
- cache: null
where:
- generic_type(?cache) == Cache
- ?cache.provider_type in ['AWS::ElastiCache::ReplicationGroup', 'AWS::ElastiCache::ServerlessCache']
- ?principal has EFFECTIVE elasticache:CreateSnapshot on ?cache ARN AND ?principal has EFFECTIVE elasticache:CopySnapshot
on snapshots created from ?cache
- ?cache.properties.Engine in ['redis', 'valkey'] (Memcached does not support snapshots; only Redis/Valkey)
emit:
source_type: Identity
target_type: Cache
source: ?principal
target: ?cache
permissions:
- elasticache:CreateSnapshot
- elasticache:CopySnapshot
conditions:
- iam_permission
- scp_or_org_policy
- resource_policy
- service_state
state_logic: "CONDITIONAL(resource_policy): ACTIVE only when BOTH CreateSnapshot and CopySnapshot are\
\ confirmed EFFECTIVE on the cache ARN, and the destination S3 bucket is 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 creatable but only exportable to an account-internal\
\ destination \u2014 emit CONDITIONAL(resource_policy) to flag the capability while noting the destination\
\ gate. CONDITIONAL(iam_permission) when either permission is unresolved. BLOCKED when an SCP or permission\
\ boundary denies either CreateSnapshot or CopySnapshot. CONDITIONAL(service_state) when the cache\
\ is in a non-snapshotable state (creating, modifying, deleting)."
confidence: min(contributing_confidences) * 0.80
derived_from:
- ?principal HasPermission ?cache (elasticache:CreateSnapshot EFFECTIVE)
- ?principal HasPermission ?cache (elasticache:CopySnapshot EFFECTIVE)
false_positive_note: 'Both CreateSnapshot and CopySnapshot are required. Do NOT emit ACTIVE unless both
are confirmed effective. The destination S3 bucket must also accept the write (bucket policy must
allow s3:PutObject from the ElastiCache service principal or the caller''s account). For encrypted
caches (AtRestEncryptionEnabled with a CMK), the RDB data in S3 is KMS-encrypted; the attacker additionally
needs kms:Decrypt on the CMK. This rule is conservative (Phase-6 guidance for ElastiCache: 1-3 rules);
it collapses the two-step create+copy path into a single CanExfiltrate edge, modeling the complete
exfiltration capability without requiring pre-existing snapshots. Cached data depends on what the
application stores; impact varies (transient data vs. persistent secrets).'
narrative: "{principal.name} can call elasticache:CreateSnapshot to create a fresh Redis snapshot from\
\ {cache.name}, then elasticache:CopySnapshot to export the resulting RDB file to an S3 bucket. If\
\ the destination S3 bucket is attacker-controlled, the full serialized dump of all in-memory cache\
\ data \u2014 including any application secrets (session tokens, API keys, database passwords) stored\
\ by the application \u2014 is exfiltrated without ever connecting to the live cluster or waiting\
\ for a scheduled backup."