aws-emr-exfiltrate-via-unload

An EMR cluster whose instance profile has s3:PutObject on an attacker-controlled S3 bucket can exfiltrate data via Spark df.write or Hive INSERT OVERWRITE DIRECTORY.

derived aws emits CanExfiltrate

match

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

{'cluster': None} ExecutesAs {'role': None}

where

node_type(?cluster) == AnalyticsService ?cluster provider_type == AWS::EMR::Cluster ?role has EFFECTIVE s3:PutObject on a destination bucket (potentially cross-account or external)

emit

source typeIdentity
target typeStorage
source?role
target<destination S3 bucket (ObjectStorage)>
permissionss3:PutObject
conditionsiam_permission resource_policy
state logicACTIVE when ?role has EFFECTIVE s3:PutObject on the destination bucket AND the destination bucket is reachable (no VPC endpoint restriction preventing writes from the cluster's VPC). CONDITIONAL(resource_policy) if a bucket policy must also grant s3:PutObject from the cluster's account/role to take effect.

Narrative

EMR cluster {cluster.name} (via instance profile {role.name}) can write/export data to S3 bucket {bucket.name} (s3:PutObject); an attacker with code execution on the cluster can use Spark df.write or Hive INSERT OVERWRITE DIRECTORY to exfiltrate data lake contents to that destination.

Raw rule rules/derived/aws/emr.yaml

id: aws-emr-exfiltrate-via-unload
emits: CanExfiltrate
description: An EMR cluster whose instance profile has s3:PutObject on an attacker-controlled S3 bucket
  can exfiltrate data via Spark df.write or Hive INSERT OVERWRITE DIRECTORY.
match:
- - cluster: null
  - ExecutesAs
  - role: null
where:
- node_type(?cluster) == AnalyticsService
- ?cluster provider_type == AWS::EMR::Cluster
- ?role has EFFECTIVE s3:PutObject on a destination bucket (potentially cross-account or external)
emit:
  source_type: Identity
  target_type: Storage
  source: ?role
  target: <destination S3 bucket (ObjectStorage)>
  permissions:
  - s3:PutObject
  conditions:
  - iam_permission
  - resource_policy
  state_logic: ACTIVE when ?role has EFFECTIVE s3:PutObject on the destination bucket AND the destination
    bucket is reachable (no VPC endpoint restriction preventing writes from the cluster's VPC). CONDITIONAL(resource_policy)
    if a bucket policy must also grant s3:PutObject from the cluster's account/role to take effect.
  derived_from:
  - <ExecutesAs(aws-emr-executes-as-instance-profile) edge_id>
  - <HasPermission(?role -> s3:PutObject) edge_id>
  false_positive_note: Only emit when the destination bucket is cross-account (exfiltration to an attacker-controlled
    external bucket) OR when the same-account bucket is a high-value target. Writes to the cluster's own
    logging/output bucket are not exfiltration. Confirm that the bucket policy grants PutObject from the
    instance profile's account/role; cross-account writes require both the IAM role policy AND the destination
    bucket policy to permit the write.
  narrative: EMR cluster {cluster.name} (via instance profile {role.name}) can write/export data to S3
    bucket {bucket.name} (s3:PutObject); an attacker with code execution on the cluster can use Spark
    df.write or Hive INSERT OVERWRITE DIRECTORY to exfiltrate data lake contents to that destination.
move · open · esc close