Attack Chokepoints 3 invariant stages
Applying the chokepoint framework to AWS Stolen Credential Permission Enumeration. Learn the framework →
- Tooling — Pacu, enumerate-iam, CloudFox, WeirdAAL, ScoutSuite/Prowler repurposed as offensive enumerators, the aws CLI driven by hand, or raw boto3/SDK scripts
- User-Agent string — attacker-controlled. AWS SDKs and tools commonly expose application-ID or custom user-agent metadata, so tool-name matches such as "Pacu" or "Boto3" are not a durable signal
- Source IP — commercial VPN, Tor, residential proxy, an attacker-controlled EC2 instance, or an instance inside the victim's own account (making the traffic AWS-internal)
- Call pacing and jitter — probes can be spread over hours or days to defeat short windows
- Region selection and ordering
- Which service namespaces to probe, and in what sequence
- Whether enumeration is driven through the API directly or through a hijacked console session
- Read-only probing versus attempting state-changing calls
- Effective permissions are not computable from outside AWS. Identity-based and resource-based allows are generally unioned; permissions boundaries, Organizations SCPs and RCPs, and session policies constrain the result, and any explicit deny overrides. A principal generally cannot read the SCPs applied to it or resource policies it has no grant on, and visibility into these layers varies by principal and account, so context may settle part of the effective set while anything it leaves open can only be established by attempting the action
- Permission discovery has no quiet IAM oracle at the service boundary. iam:SimulatePrincipalPolicy requires its own permission and can itself be denied, while a direct probe still requires an AWS API request and service authorization outcome
- For CloudTrail events that are emitted, errorCode, errorMessage, eventSource, eventTime, and the userIdentity block are authored by AWS rather than the caller. Event visibility is conditional because it depends on support for the service and operation, the selected event category, and whether that integration records denied requests
- Each capability that context leaves unresolved costs at least one AWS API request and service outcome. CloudTrail is a conditional detection surface for that request, not part of the invariant itself
- sts:GetCallerIdentity requires no IAM permission and therefore always succeeds for any valid credential — the one call in the sequence that cannot be denied, and therefore cannot be hidden behind a permission failure
1 Identity Orientation (Optional Research Anchor) ▶
- Operator holds valid, unexpired AWS credentials (long-term AKIA* key, temporary ASIA* session credentials, or a hijacked console session)
- Operator lacks prior knowledge of the credential's effective permission set
- Network path to AWS API endpoints (public internet; no inbound access to the victim environment is required)
- CloudTrail coverage for the supported service operations targeted by the rules. Console event history automatically retains supported management events for 90 days, but the Athena queries need an explicitly created trail delivering to S3 plus an Athena table over that data. Management events are selected by default when a trail is created; S3 delivery, the table, and any required data-event selectors are not automatic
- AWS CloudTrail management events (eventSource sts.amazonaws.com)
- AWS CloudTrail management events (eventSource iam.amazonaws.com)
CloudTrail — GetCallerIdentity as first observed call for a leaked access key ▶
2 Cross-Namespace Permission Probing ▶
- AWS CloudTrail management events (all eventSource values)
CloudTrail — AccessDenied across six namespaces for one access key in 90 seconds ▶
3 Capability Confirmation ▶
- AWS CloudTrail management events (all eventSource values)
- AWS CloudTrail S3 data events (optional — adds object-level successes such as s3:GetObject where a data-event selector covers the bucket)
Variations 4 variants tracked
Tools and methods that exploit this chokepoint. The list grows. The chokepoint doesn't change.
Pacu 2018-Q3 Active ▶
run iam__bruteforce_permissions
- CloudTrail: high-volume errorCode AccessDenied spanning many eventSource values for one principal
- CloudTrail: sts:GetCallerIdentity preceding the burst
enumerate-iam 2019-Q1 Active ▶
python enumerate-iam.py --access-key AKIAEXAMPLE1234ABCD --secret-key <redacted>
- CloudTrail: hundreds to thousands of denied read-only calls in a short window from one principal
- CloudTrail: authorization-failure ratio approaching 1.0 across dozens of distinct eventSource values
CloudFox 2022-Q4 Active ▶
cloudfox aws --profile stolen all-checks
- CloudTrail: Describe*/List*/Get* calls across compute, storage, identity, and secrets namespaces
- CloudTrail: mixed success and AccessDenied from a single principal in one window
Manual AWS CLI enumeration 2015-Q1 Active ▶
aws sts get-caller-identity && aws s3 ls && aws iam list-users && aws ec2 describe-instances
- CloudTrail: userAgent shows the stock aws-cli string — indistinguishable from legitimate administration
- CloudTrail: denials spread across namespaces despite low total call volume
Detection Strategy
Rules organized by the chokepoint stage they detect. Each stage has one or more rules at different maturity levels.
Establish baseline visibility into identity-orientation calls and denial rate...
Research
High FP
▶
Surface every sts:GetCallerIdentity event alongside a per-principal count of authorization failures, grouping on the session issuer ARN for assumed-role sessions so a role's history survives session rotation. Only the authorization and authentication error codes count as failures. Characterises what normal looks like — which principals call GetCallerIdentity routinely, which service accounts carry a chronic denial rate, and what the baseline distinct-namespace spread is — so use it for baselining rather than alerting.
title: AWS Identity Orientation and Authorization Failure Baseline
id: 3c1f7a92-6b04-4d55-9e88-2a0c5f31b7de
status: experimental
description: >
Baselining rule for visibility. Surfaces sts:GetCallerIdentity and CloudTrail
authorization failures so the Hunt and Analyst thresholds can be derived from local data.
Deploy it to characterise the environment, then tune the higher tiers from what it shows.
Answer three questions before deploying the higher tiers:
1. Which principals call GetCallerIdentity routinely, and from which source IPs?
2. Which principals carry chronic authorization failures, and on which
(eventSource, eventName) pairs? That list becomes the Hunt tier allowlist.
3. What is the normal distinct-namespace denial spread per principal? Most legitimate
principals sit at 0-1. Re-derive the Hunt threshold from this, do not assume 5.
Choosing the principal key: baseline on the identity that issued the session. A temporary
ASIA* access key rotates per session, and an assumed-role ARN embeds a RoleSessionName that
repeats only when the caller reuses it, so neither is a dependable long-term baseline
identity. All three tiers key on COALESCE(useridentity.sessioncontext.sessionissuer.arn,
useridentity.arn, useridentity.principalid, useridentity.accesskeyid) — the session issuer
ARN (the role) for AssumedRole sessions, the caller ARN or principal ID otherwise, with
accessKeyId as a last fallback. Keep the expression identical across tiers so baselines,
aggregations, and joins agree on what a principal is.
Only authorization and authentication failures count as denials — AccessDenied,
AccessDeniedException, UnauthorizedOperation, Client.UnauthorizedOperation,
AuthorizationError, AuthFailure. Throttling, validation, and missing-resource errors are
also written to errorCode and say nothing about the caller's permissions, so counting every
non-empty errorCode as probing inflates the denial ratio with application noise. Total event
counts stay as the denominator. AWS documents errorCode and errorMessage as optional record
fields, and some services report authorization failures inside responseElements rather
than the top-level errorcode column this query reads. CloudTrail visibility also depends on
support for the service and operation, the selected event category, and whether that
integration records denied requests. Confirm those conditions before relying on the count.
Avoid building detection on userAgent. AWS SDKs and tools commonly expose application-ID or
custom user-agent metadata, so tool-name matches such as "Pacu" or "Boto3" are not durable.
Baseline query (Athena). It needs a CloudTrail trail delivering management events to S3 and
an Athena table over that data. Management events are selected by default when a trail is
created, but the S3 delivery and the Athena table are not created automatically; console
event history is automatic and covers 90 days, but is not queryable this way.
WITH ct AS (
SELECT COALESCE(useridentity.sessioncontext.sessionissuer.arn,
useridentity.arn,
useridentity.principalid,
useridentity.accesskeyid) AS principal,
eventsource,
eventname,
sourceipaddress,
useridentity.accesskeyid AS access_key,
COALESCE(errorcode, '') IN (
'AccessDenied', 'AccessDeniedException', 'UnauthorizedOperation',
'Client.UnauthorizedOperation', 'AuthorizationError', 'AuthFailure'
) AS auth_denied
FROM cloudtrail_logs
WHERE from_iso8601_timestamp(eventtime) > now() - INTERVAL '30' DAY
)
SELECT principal,
count(*) AS total_events,
count_if(auth_denied) AS auth_denials,
count(DISTINCT CASE WHEN auth_denied THEN eventsource END) AS denied_namespaces,
array_agg(DISTINCT concat(eventsource, ':', eventname))
FILTER (WHERE auth_denied) AS denied_pairs,
count_if(eventname = 'GetCallerIdentity') AS orientation_calls,
count(DISTINCT sourceipaddress) AS src_ips,
count(DISTINCT access_key) AS session_keys
FROM ct
GROUP BY 1
ORDER BY total_events DESC
This tier keeps service and chronic-denial principals in the results, since identifying them
is the point, and nothing here needs data-event logging.
references:
- https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/
- https://attack.mitre.org/techniques/T1580/
- https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-unsupported-aws-services.html
- https://docs.aws.amazon.com/athena/latest/ug/cloudtrail-logs.html
author: '@NovaSky0x1'
date: 2026-08-09
tags:
- attack.discovery
- attack.t1580
- attack.t1526
- detection.maturity.research
logsource:
product: aws
service: cloudtrail
detection:
selection_orientation:
eventSource: 'sts.amazonaws.com'
eventName: 'GetCallerIdentity'
selection_denied:
errorCode:
- 'AccessDenied'
- 'AccessDeniedException'
- 'UnauthorizedOperation'
- 'Client.UnauthorizedOperation'
- 'AuthorizationError'
- 'AuthFailure'
condition: selection_orientation or selection_denied
falsepositives:
- Expected at this tier. GetCallerIdentity is routine SDK, CI/CD, and Terraform behaviour
- AWS Config rules and drift detection generating chronic denials on a fixed API set
- Backup and inventory agents probing services they are not permitted to read
- Developers running exploratory CLI commands against accounts they legitimately hold
level: informational
Denial breadth across service namespaces for a single principal in a rolling ...
Hunt
Med FP
▶
Aggregate authorization failures per principal over a rolling window, keyed on the session issuer ARN for assumed-role sessions since an ASIA* key rotates per session and an assumed-role ARN repeats only when the RoleSessionName is reused. Alert where those failures span five or more distinct eventSource values and account for at least 0.5 of the principal's events in the window, counting only AccessDenied, AccessDeniedException, UnauthorizedOperation, Client.UnauthorizedOperation, AuthorizationError, and AuthFailure. Exclude principals on a documented chronic-denial allowlist (drift detection, Config rules, backup agents), applying it inside the Athena query too since the Sigma placeholder does not affect it. Breadth is a heuristic for undirected enumeration rather than a requirement of the technique: a principal denied a hundred times on one API is usually misconfigured, while one denied five times across five namespaces is mapping its own permissions.
title: AWS Credential Permission Probing — Denial Breadth Across Service Namespaces
id: 8d4b21e6-70af-4c19-b3a6-9f5c2e6188a4
status: experimental
description: >
Detects one principal generating authorization failures across five or more distinct
service namespaces in a rolling window, with an authorization-failure ratio of at least
0.5 against all events by that principal in the window. That is the shape of an operator
mapping a credential whose scope they do not know.
Breadth is a heuristic rather than an invariant: an operator with useful context, a readable
identity policy, or a correct first guess can settle capability in a handful of calls, so
this tier targets the generic, undirected case. Within it the distinct-namespace count
discriminates better than raw denial volume, since a principal denied 200 times on one API
is almost always misconfigured while one denied five times across five namespaces is mapping
its own permissions.
Choosing the principal key: aggregate on the identity that issued the session, using
COALESCE(useridentity.sessioncontext.sessionissuer.arn, useridentity.arn,
useridentity.principalid, useridentity.accesskeyid) as the Research and Analyst tiers do, so
Research-tier allowlists apply unchanged. A temporary ASIA* key rotates per session and an
assumed-role ARN repeats only when its RoleSessionName is reused, so grouping on either can
split one operator's probing into single-event groups that never reach the threshold of 5.
Populate filter_chronic_principals with session issuer ARNs for the same reason.
Only authorization and authentication failures count toward the namespace breadth and the
ratio numerator — AccessDenied, AccessDeniedException, UnauthorizedOperation,
Client.UnauthorizedOperation, AuthorizationError, AuthFailure. Throttling, validation, and
missing-resource errors also populate errorCode and carry no permission information, so
counting every non-empty errorCode inflates both measures with application noise. The
denominator stays every event by the principal in the window, successful or not. Services
that report authorization failures inside responseElements rather than the top-level
errorcode column are a blind spot. CloudTrail visibility also depends on support for the
service and operation, the selected event category, and whether that integration records
denied requests.
Distinct-namespace counts and ratios cannot be expressed in Sigma, so the detection block
below is a partial stub firing on any single authorization failure; deploy the query for
production. It needs a CloudTrail trail delivering management events to S3 and an Athena
table over that data — management events are selected by default when a trail is created,
but the S3 delivery and the table are not. No data-event logging is needed at this tier.
Athena:
WITH ct AS (
SELECT COALESCE(useridentity.sessioncontext.sessionissuer.arn,
useridentity.arn,
useridentity.principalid,
useridentity.accesskeyid) AS principal,
eventsource,
COALESCE(errorcode, '') IN (
'AccessDenied', 'AccessDeniedException', 'UnauthorizedOperation',
'Client.UnauthorizedOperation', 'AuthorizationError', 'AuthFailure'
) AS auth_denied
FROM cloudtrail_logs
WHERE from_iso8601_timestamp(eventtime) > now() - INTERVAL '15' MINUTE
AND useridentity.type <> 'AWSService' -- matches filter_service_principals below
)
SELECT principal,
count(*) AS total_events,
count_if(auth_denied) AS auth_denials,
count(DISTINCT CASE WHEN auth_denied THEN eventsource END) AS denied_namespaces
FROM ct
GROUP BY 1
HAVING count(DISTINCT CASE WHEN auth_denied THEN eventsource END) >= 5
AND count_if(auth_denied) * 1.0 / count(*) >= 0.5
Before production: re-derive both thresholds from the Research tier, build the chronic-denial
allowlist, and add those principal exclusions to the Athena WHERE clause by hand, since
filter_chronic_principals below constrains only the Sigma stub. Config rules, drift
detection, and backup agents are the dominant false positives and they are stable and
enumerable.
Pacing defeats a 15-minute window; widening it to 24 hours catches slower operators at
query cost. The Analyst tier's novelty scoring helps against a low call rate inside its own
window, but that window is one day, so multi-day pacing evades it too.
references:
- https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/
- https://sysdig.com/blog/scarleteel-2-0/
- https://github.com/andresriancho/enumerate-iam
- https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-unsupported-aws-services.html
- https://docs.aws.amazon.com/athena/latest/ug/cloudtrail-logs.html
author: '@NovaSky0x1'
date: 2026-08-09
tags:
- attack.discovery
- attack.t1580
- attack.t1526
- detection.maturity.hunt
logsource:
product: aws
service: cloudtrail
detection:
# Distinct-namespace and ratio aggregation cannot be expressed in Sigma.
# Use the Athena query above for production. This stub is a partial signal only.
selection:
errorCode:
- 'AccessDenied'
- 'AccessDeniedException'
- 'UnauthorizedOperation'
- 'Client.UnauthorizedOperation'
- 'AuthorizationError'
- 'AuthFailure'
filter_chronic_principals:
# PENDING: populate from the Research-tier baseline before production, and mirror the
# same exclusions into the Athena CTE. Use session issuer ARNs (the role), not session
# ARNs or ASIA* keys.
# userIdentity.sessionContext.sessionIssuer.arn|contains:
# - 'role/aws-config-role'
# - 'role/backup-inventory-agent'
userIdentity.arn: '' # placeholder — remove and populate before production
filter_service_principals:
userIdentity.type: 'AWSService'
condition: selection and not filter_chronic_principals and not filter_service_principals
falsepositives:
- AWS Config rules and drift detection probing resources they cannot read — chronic, stable, allowlistable
- Backup and inventory agents sweeping services outside their grant
- Terraform or CloudFormation against a partially-provisioned role, especially on first apply
- Newly deployed service accounts before policies are finalised — expect a burst, then silence
- CSPM and security scanners operating with intentionally reduced permissions
level: medium
Novel-denial breadth followed by a first success in a just-denied namespace
Analyst
Low FP
▶
Correlate the denied-to-allowed transition and score on novelty. Fire where a principal generates authorization failures on (eventSource, eventName) tuples never previously observed for it — successful or failed — within a thirty-day baseline, across three or more distinct namespaces, and subsequently issues a successful call in a namespace that denied it earlier in the same window. Counting prior failures in the baseline keeps a routinely denied tuple from reading as novel every day. Baseline on the session issuer ARN, since a baseline keyed on the ASIA* key is empty on every new session, and join denial to success on a tighter actor key (access key first) so one session's denial under a shared role cannot join another session's success. Novelty helps against a low call rate inside the observation window, which is one day here, so multi-day pacing still evades it unless that window is widened at query cost. Apply the chronic-principal allowlist inside the Athena query as well as the Sigma stub. Breadth and the transition are heuristics, and an operator can skip either.
title: AWS Capability Confirmation — Novel Denial Breadth Followed by Success in a Denied Namespace
id: b6e09f34-25cd-4a71-8c02-71d4a3e5f9b8
status: experimental
description: >
Fires when a principal is denied on (eventSource, eventName) pairs it has never been observed
calling in 30 days, across three or more namespaces, and then succeeds in a namespace that
just denied it — the clearest available evidence that an operator located an exploitable path.
Both signals are heuristics rather than invariants. Permission enumeration does not require
cross-namespace breadth when context or a readable policy narrows the search, and it does not
require a denied-then-allowed transition, since an operator can stop after the denials or
succeed on the first call attempted. What holds is that any capability not already known from
context or a readable policy has to be tested through the relevant AWS API, and the AWS
service returns an authorization outcome. CloudTrail sees the request only when the service
and operation are supported, the relevant event category is selected, and that integration
records denied requests.
The baseline holds every (principal, eventSource, eventName) tuple observed in the preceding
30 days, successful or failed. Restricting it to successes would make a routinely denied
tuple — a Config rule probing a resource it can never read — look novel every day.
Two keys, deliberately. The 30-day baseline uses a stable principal (session issuer ARN
first, then caller ARN, principal ID, access key) so a role's history survives session
turnover, while the denial-to-success correlation additionally joins on an actor key (access
key first, since a temporary credential's ASIA* key is unique to one session, then caller ARN
or principal ID, then the stable principal) so a denial in one session under a shared role
cannot join a different session's success. Neither an ASIA* key nor an assumed-role ARN is a
dependable long-term baseline identity: the key rotates per session, and the ARN embeds a
RoleSessionName that repeats only when the caller reuses it.
A novel denial means an authorization or authentication failure — AccessDenied,
AccessDeniedException, UnauthorizedOperation, Client.UnauthorizedOperation,
AuthorizationError, AuthFailure. Throttling, validation, and missing-resource errors carry no
permission information and do not count toward the three-namespace breadth. Success is the
absence of any error code, and errorcode is NULL on success in the AWS sample CloudTrail
table, so it must be tested with IS NULL rather than = ''. Services reporting authorization
failures inside responseElements rather than the top-level errorcode column are a blind spot
for the novel-denial half and can also be misread as successes by that test, so normalize
them into errorcode first.
Fidelity rises sharply when the successful call is itself sensitive — CreateAccessKey,
AttachUserPolicy, UpdateAssumeRolePolicy, GetSecretValue, or CreateSnapshot, all management
events. s3:GetObject against a bucket this principal has never read is a strong signal too,
but it is an object-level data event, present only where a data-event selector covers that
bucket, so treat it as optional coverage and otherwise leave it out of the selection below.
Baseline comparison and cross-event correlation cannot be expressed in Sigma, so the stub
below covers only the sensitive-success half; deploy the query for production. It needs a
CloudTrail trail delivering management events to S3 and an Athena table over that data —
management events are selected by default when a trail is created; the S3 delivery and the
table are not. Athena:
WITH ct AS ( -- both keys and the denial test applied once
SELECT COALESCE(useridentity.sessioncontext.sessionissuer.arn,
useridentity.arn,
useridentity.principalid,
useridentity.accesskeyid) AS principal,
COALESCE(useridentity.accesskeyid,
useridentity.arn,
useridentity.principalid,
useridentity.sessioncontext.sessionissuer.arn) AS actor_key,
useridentity.arn AS identity_arn, eventsource, eventname,
sourceipaddress, errorcode,
from_iso8601_timestamp(eventtime) AS ts,
COALESCE(errorcode, '') IN (
'AccessDenied', 'AccessDeniedException', 'UnauthorizedOperation',
'Client.UnauthorizedOperation', 'AuthorizationError', 'AuthFailure'
) AS auth_denied
FROM cloudtrail_logs
WHERE from_iso8601_timestamp(eventtime) > now() - INTERVAL '31' DAY
AND useridentity.type <> 'AWSService' -- matches filter_service_principals below
),
baseline AS ( -- every tuple seen for this principal, successful or denied
SELECT DISTINCT principal, eventsource, eventname, 1 AS seen
FROM ct
WHERE ts BETWEEN now() - INTERVAL '31' DAY AND now() - INTERVAL '1' DAY
),
novel_denials AS ( -- auth failures on never-before-seen tuples
SELECT c.principal, c.actor_key, c.eventsource, min(c.ts) AS first_denial
FROM ct c
LEFT JOIN baseline b
ON b.principal = c.principal AND b.eventsource = c.eventsource
AND b.eventname = c.eventname
WHERE c.ts > now() - INTERVAL '1' DAY AND c.auth_denied AND b.seen IS NULL
GROUP BY 1, 2, 3
),
probing AS ( -- actors novel-denied in >= 3 namespaces
SELECT principal, actor_key FROM novel_denials
GROUP BY 1, 2
HAVING count(DISTINCT eventsource) >= 3
)
SELECT s.ts, s.identity_arn, s.principal, s.actor_key, s.eventsource, s.eventname,
s.sourceipaddress, d.first_denial
FROM ct s
JOIN novel_denials d -- same actor, same namespace that denied it
ON d.principal = s.principal AND d.actor_key = s.actor_key
AND d.eventsource = s.eventsource
JOIN probing p
ON p.principal = s.principal AND p.actor_key = s.actor_key
WHERE s.ts > now() - INTERVAL '1' DAY
AND s.ts > d.first_denial -- success after the denial
AND (s.errorcode IS NULL OR s.errorcode = '')
ORDER BY s.ts
Requires a stable 30-day CloudTrail history; expect elevated false positives in younger
environments or right after a large deployment change. Novelty helps against a low call rate
inside the observation window, but that window is one day here, so multi-day pacing evades it
unless the window is widened at query cost. Apply the environment's chronic and
legitimate-principal allowlist inside the Athena CTE before production, since
filter_legit_software below constrains only the Sigma stub. Avoid keying on userAgent.
references:
- https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/
- https://sysdig.com/blog/scarleteel-2-0/
- https://github.com/RhinoSecurityLabs/pacu
- https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-unsupported-aws-services.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html
- https://docs.aws.amazon.com/athena/latest/ug/cloudtrail-logs.html
author: '@NovaSky0x1'
date: 2026-08-09
tags:
- attack.discovery
- attack.t1580
- attack.t1526
- detection.maturity.analyst
logsource:
product: aws
service: cloudtrail
detection:
# Baseline anti-join and denied-to-allowed correlation cannot be expressed in Sigma.
# Use the Athena query above for production. This stub covers the sensitive-success
# half only and must be correlated with the novel-denial condition to reach the
# stated fidelity. Every eventName below is a management event; adding data-event
# names such as GetObject requires an S3 object-level data-event selector.
selection_sensitive_success:
eventName:
- 'CreateAccessKey'
- 'AttachUserPolicy'
- 'AttachRolePolicy'
- 'PutUserPolicy'
- 'UpdateAssumeRolePolicy'
- 'CreateLoginProfile'
- 'UpdateLoginProfile'
- 'GetSecretValue'
- 'CreateSnapshot'
- 'ModifySnapshotAttribute'
filter_failed:
errorCode|exists: true
filter_service_principals:
userIdentity.type: 'AWSService'
filter_legit_software:
# PENDING: populate with the approved administrative and automation principals that
# legitimately call these APIs — IaC deployment roles, break-glass roles, IAM
# provisioning pipelines, backup services taking snapshots — and mirror the same
# exclusions into the Athena CTE. Key on the session issuer ARN (the role), not on
# session ARNs or ASIA* access keys.
# userIdentity.sessionContext.sessionIssuer.arn|contains:
# - 'role/terraform-deploy'
# - 'role/iam-provisioning-pipeline'
userIdentity.arn: '' # placeholder — remove and populate before production
condition: selection_sensitive_success and not filter_failed and not filter_service_principals and not filter_legit_software
falsepositives:
- Break-glass or IR administrators exploring an unfamiliar account under legitimate authority
- Newly onboarded engineers whose principal has no 30-day baseline yet
- Migration and refactoring work legitimately calling APIs a principal has never called
- Penetration tests and red-team engagements — check the engagement window before escalating
- Environments with less than 30 days of CloudTrail history
level: high
OSINT Pivots
page.url:"/.env"
http.html:"AWS_SECRET_ACCESS_KEY"
"aws_secret_access_key" path:.env
"AWS_SECRET_ACCESS_KEY" path:.github/workflows
"AKIA" path:*.tfvars
References
- https://attack.mitre.org/techniques/T1580/
- https://attack.mitre.org/techniques/T1526/
- https://unit42.paloaltonetworks.com/large-scale-cloud-extortion-operation/
- https://sysdig.com/blog/scarleteel-2-0/
- https://github.com/RhinoSecurityLabs/pacu
- https://github.com/andresriancho/enumerate-iam
- https://github.com/BishopFox/cloudfox
- https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html
- https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-unsupported-aws-services.html
- https://docs.aws.amazon.com/awscloudtrail/latest/userguide/logging-data-events-with-cloudtrail.html
- https://docs.aws.amazon.com/athena/latest/ug/cloudtrail-logs.html