aws-cloudtrail-lookup-events

Principal with cloudtrail:LookupEvents can query 90 days of account-wide management-event history without S3 access, exposing IAM principal names, access-key IDs, resource ARNs, and error codes - useful for recon and lateral movement planning.

derived aws emits CanRead

match

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

{'principal': None} HasPermission {'account': None}

where

node_type(?account) == Account effective_action(?principal, ?account) includes 'cloudtrail:LookupEvents'

emit

source typeIdentity
target typeAccount
source?principal
target?account
permissionscloudtrail:LookupEvents
conditionsiam_permission scp_or_org_policy
state logicACTIVE when the principal holds cloudtrail:LookupEvents with no effective deny. LookupEvents operates account-wide and is not scoped to a specific trail in IAM (the IAM resource is always *). BLOCKED only when an SCP or boundary explicitly denies the action. This edge is emitted once per account (per principal-account pair), not once per trail.

Narrative

{principal.name} can call cloudtrail:LookupEvents to search 90 days of management-event history in {account.name} - revealing IAM principal names, access-key IDs used for API calls, resource ARNs, source IPs, and error codes. This is a recon capability for lateral movement planning; it grants no resource access.

Raw rule rules/derived/aws/cloudtrail.yaml

id: aws-cloudtrail-lookup-events
emits: CanRead
description: "Principal with cloudtrail:LookupEvents can query 90 days of account-wide management-event\
  \ history without S3 access, exposing IAM principal names, access-key IDs, resource ARNs, and error\
  \ codes \u2014 useful for recon and lateral movement planning."
match:
- - principal: null
  - HasPermission
  - account: null
where:
- node_type(?account) == Account
- effective_action(?principal, ?account) includes 'cloudtrail:LookupEvents'
emit:
  source_type: Identity
  target_type: Account
  source: ?principal
  target: ?account
  permissions:
  - cloudtrail:LookupEvents
  conditions:
  - iam_permission
  - scp_or_org_policy
  state_logic: ACTIVE when the principal holds cloudtrail:LookupEvents with no effective deny. LookupEvents
    operates account-wide and is not scoped to a specific trail in IAM (the IAM resource is always *).
    BLOCKED only when an SCP or boundary explicitly denies the action. This edge is emitted once per account
    (per principal-account pair), not once per trail.
  confidence: 0.9
  derived_from:
  - HasPermission(?principal, cloudtrail:LookupEvents, ?account)
  false_positive_note: "RECON primitive only \u2014 LookupEvents surfaces metadata about past API calls\
    \ (who called what, when, from where, with which access key) but does NOT expose raw credentials or\
    \ session tokens. It cannot be used to access other resources. Do NOT chain CanRead(account) to CanAssume,\
    \ CanImpersonate, or any access edge without a separate, evidenced permission grant. Note: LookupEvents\
    \ returns management events from the CURRENT account's event history; it does NOT reach org-level\
    \ data or other accounts' trails. Analysts should note that error-code fields (AccessDenied) in returned\
    \ events expose which actions the caller does NOT have, giving attackers negative-space recon. This\
    \ edge targets the Account boundary node (not a specific trail) because LookupEvents queries the entire\
    \ account's audit history regardless of trail configuration."
  narrative: "{principal.name} can call cloudtrail:LookupEvents to search 90 days of management-event\
    \ history in {account.name} \u2014 revealing IAM principal names, access-key IDs used for API calls,\
    \ resource ARNs, source IPs, and error codes. This is a recon capability for lateral movement planning;\
    \ it grants no resource access."
move · open · esc close