LoFP LoFP / cross-account kms key usage may be legitimate in multi-account aws organizations architectures where centralized encryption keys are used for data governance or auditing workflows. confirm whether the external kms key belongs to an expected account before taking action. data migration or cross-account backup workflows may legitimately re-encrypt s3 objects using a key in another account. ensure these workflows are documented, tied to known iam roles, and occur on predictable schedules.

Techniques

Sample rules

AWS S3 Object Encryption Using External KMS Key

Description

Identifies use of the S3 CopyObject API where the destination object is encrypted using an AWS KMS key from an external AWS account. This behavior may indicate ransomware-style impact activity where an adversary with access to a misconfigured S3 bucket encrypts objects using a KMS key they control, preventing the bucket owner from decrypting their own data. This technique is a critical early signal of destructive intent or cross-account misuse.

Detection logic

from logs-aws.cloudtrail-* metadata _id, _version, _index

// any successful S3 copy event
| where
  event.dataset == "aws.cloudtrail"
  and event.provider == "s3.amazonaws.com"
  and event.action == "CopyObject"
  and event.outcome == "success"

// dissect request parameters to extract KMS key info and target object info
| dissect aws.cloudtrail.request_parameters
    "{%{?bucketName}=%{Esql.aws_cloudtrail_request_parameters_target_bucket_name},%{?x-amz-server-side-encryption-aws-kms-key-id}=%{?arn}:%{?aws}:%{?kms}:%{?region}:%{Esql.aws_cloudtrail_request_parameters_kms_key_account_id}:%{?key}/%{Esql.aws_cloudtrail_request_parameters_kms_key_id},%{?Host}=%{?tls.client.server.name},%{?x-amz-server-side-encryption}=%{?server_side_encryption},%{?x-amz-copy-source}=%{?bucket.object.name},%{?key}=%{Esql.aws_cloudtrail_request_parameters_target_object_key}}"

// detect cross-account key usage
| where cloud.account.id != Esql.aws_cloudtrail_request_parameters_kms_key_account_id

// keep ECS and dissected fields
| keep
  @timestamp,
  data_stream.namespace,
  user.name,
  user_agent.original,
  source.ip,
  aws.cloudtrail.user_identity.arn,
  aws.cloudtrail.user_identity.type,
  aws.cloudtrail.user_identity.access_key_id,
  aws.cloudtrail.resources.arn,
  aws.cloudtrail.resources.type,
  event.action,
  event.outcome,
  cloud.account.id,
  cloud.region,
  aws.cloudtrail.request_parameters,
  aws.cloudtrail.response_elements,
  Esql.aws_cloudtrail_request_parameters_target_bucket_name,
  Esql.aws_cloudtrail_request_parameters_target_object_key,
  Esql.aws_cloudtrail_request_parameters_kms_key_account_id,
  Esql.aws_cloudtrail_request_parameters_kms_key_id,
  _id,
  _version,
  _index