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.

derived aws emits CanExfiltrate

match

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

{'principal': None} HasPermission {'cache': None}

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 typeIdentity
target typeCache
source?principal
target?cache
permissionselasticache:CreateSnapshot elasticache:CopySnapshot
conditionsiam_permission scp_or_org_policy resource_policy service_state
state logicCONDITIONAL(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."
move · open · esc close