CloudTrail Monitoring Patterns That Matter
The AWS CloudTrail monitoring patterns that actually catch attackers — root usage, IAM changes, logging tampering, and credential exfiltration, with detection logic.
CloudTrail monitoring fails when teams try to alert on everything and drown, or log everything and look at nothing. The patterns that matter are a short list: root account usage, IAM changes, anyone tampering with logging, and instance credentials used from outside your network. Those four catch the actions nearly every cloud attacker takes. This guide ships the high-signal CloudTrail detections and the architecture that keeps them trustworthy.
AWS CloudTrail is the audit log of API activity in your account, and it is where cloud attacks become visible — valid-account abuse (T1078.004), account manipulation (T1098), and logging tampering (T1562.008). It is also where an SSRF credential theft shows up as an instance role used from the wrong place.
What should you actually monitor in CloudTrail?
CloudTrail records every API call, which is both its power and its trap — alert on all of it and analysts tune you out. The discipline is to start from attacker behavior and monitor the actions an intruder must take: prove who they are, escalate privilege, and blind the defender. That narrows thousands of event types to a high-signal core.
This is the cloud expression of the same philosophy behind every detection on darkpwn: watch behavior that is rare-and-bad, not common-and-noisy, the way the detection engineering workflow prescribes.
Which CloudTrail patterns matter most?
| Pattern | Why it matters | ATT&CK |
|---|---|---|
| Root account usage | Root should be near-never used; any use is suspect | T1078.004 |
| IAM changes | New users/keys/policies = persistence & escalation | T1098 |
| Logging tampering | Stopping CloudTrail/GuardDuty blinds you | T1562.008 |
| Credential exfiltration | Instance role used outside the VPC | T1552.005 |
| Console login without MFA | A single-factor login is a foothold | T1078.004 |
| Recon bursts | ListBuckets/GetCallerIdentity from a new principal | T1580 |
These six catch the overwhelming majority of real cloud intrusions. Everything else is secondary — get these right before adding noise.
Why does CloudTrail miss data exfiltration by default?
This is the gap that surprises people during their first real incident, and it is a configuration choice rather than a limitation.
CloudTrail splits events into two categories:
| Category | Examples | Logged by default? |
|---|---|---|
| Management events (control plane) | CreateUser, AssumeRole, StopLogging, RunInstances | Yes — the first copy is included |
| Data events (data plane) | S3 GetObject/PutObject, Lambda Invoke, DynamoDB item operations | No — opt-in and billed per event |
Read the second row again in the context of an exfiltration investigation. An attacker
downloading every object in your most sensitive S3 bucket generates no CloudTrail events at all
unless data events were enabled on that bucket beforehand. You will see the AssumeRole that got
them the credentials and nothing whatsoever about what they took. The question every incident asks
first — what data was accessed — is the one an unconfigured CloudTrail cannot answer.
The reason this is so frequently left off is honest: data events are billed per event, and on a busy S3 estate the volume is enormous. Enabling them everywhere can genuinely cost more than the rest of your security tooling combined. So the correct posture is selective rather than universal:
- Enable data events on the buckets that matter — the ones holding customer data, backups, credentials, or anything whose disclosure is a reportable event. This is usually a small fraction of total buckets and a much smaller fraction of total object operations.
- Skip the high-volume, low-sensitivity buckets — static assets, logs, build artifacts, and caches. Their read volume is what makes the bill frightening and their contents are not what an attacker wants.
- Decide before you need it. Data events are not retroactive. There is no way to reconstruct object access from the period before you enabled them, which means this decision is made once, in advance, or not at all.
- Enable Lambda invoke data events selectively where functions hold privileged roles, for the same reason.
What CloudTrail is not
Three properties routinely get designed around incorrectly.
It is not real-time. CloudTrail typically delivers management events within about five minutes, and delivery can take up to roughly fifteen. That is fine for detection and investigation and useless for prevention — you cannot build an automated blocking control on a signal that arrives minutes after the action completed. Where you need faster reaction, EventBridge rules on specific API calls fire sooner than log delivery, and that is the correct place for automated response.
It is not automatically global. IAM, STS at the global endpoint, CloudFront, and Route 53 are
global services whose events are recorded in us-east-1. A regional trail without global service
events configured misses IAM entirely — which means it misses the single highest-value category in
the priority table. An organization trail covering all regions solves this; verifying it is
covered is worth doing explicitly rather than assuming.
sourceIPAddress is not always an IP address. When an AWS service makes a call on your behalf,
that field contains the service’s DNS name — cloudtrail.amazonaws.com and similar — rather than
an address. Any rule doing CIDR comparison on that field needs to handle the non-IP case, or it
will throw errors, silently drop those events, or classify legitimate service activity as external.
All three failure modes have been seen in production rule sets.
How to detect the patterns that matter
Two of the highest-value detections, in Splunk SPL (the logic ports directly to CloudWatch, Sentinel KQL, or any SIEM).
Root account usage
index=cloudtrail
| spath "userIdentity.type" output=idType
| where idType="Root"
| search NOT eventName IN ("BatchGetTraces")
| stats count, values(eventName) AS actions, values(sourceIPAddress) AS srcips by awsRegion Root has no business making routine API calls. Any root activity outside a documented break-glass procedure is an immediate investigation.
Logging tampering
index=cloudtrail eventName IN ("StopLogging","DeleteTrail","UpdateTrail","DeleteDetector","UpdateDetector")
| table _time, userIdentity.arn, eventName, requestParameters.name, sourceIPAddress
| sort 0 _time How do you attribute an action to an actual human?
Every cloud alert eventually reduces to “who did this,” and in a modern AWS organisation the answer is rarely sitting in one field.
userIdentity.type takes a small set of values, and they are not equally informative:
| Type | What it means | Attribution difficulty |
|---|---|---|
Root | The account root user | Trivial — and always worth investigating |
IAMUser | A long-lived IAM user | Easy, and its existence is itself a finding in a modern org |
AssumedRole | Someone or something assumed a role | This is where nearly everything lives |
FederatedUser | Federated via GetFederationToken | Moderate |
AWSService | An AWS service acting on your behalf | Not a person |
AWSAccount | Another account | Cross-account; check the principal |
In any organisation using SSO or federation — which is to say, any organisation following current
guidance — almost every human action appears as AssumedRole. The role name tells you what
permissions were used, not who used them. The human identity is carried in the session name,
visible in userIdentity.principalId after the colon and in
userIdentity.sessionContext.sessionIssuer.
Two practical consequences:
- Parse the session name at ingest, into its own field. Every subsequent query, dashboard, and alert needs it, and extracting it inline in each rule is how you end up with rules that disagree about who did something.
- Enforce meaningful session names. If your federation configuration sets the session name to something generic, attribution is structurally impossible and no amount of log analysis recovers it. This is a one-line configuration change with an outsized investigative payoff, and it is worth checking before you need it rather than discovering it mid-incident.
For non-human principals, sessionContext.sessionIssuer.arn identifies the role, and the
combination of role plus source IP plus user agent is usually enough to identify which workload it
was. An instance role appearing from an IP outside your VPC ranges is the credential-exfiltration
signal in the priority table, and it is the cloud endpoint of an
SSRF.
Which CloudTrail signals are most underused?
Two fields carry far more detection value than the attention they get.
errorCode is the enumeration signal. An attacker holding credentials whose permissions they
do not know does the obvious thing: they try. The result is a distinctive pattern — a burst of
AccessDenied and UnauthorizedOperation responses spread across many different services and API
calls, from one principal, in a short window. Legitimate workloads fail too, but they fail
repetitively on the same call, because a misconfigured application retries the same operation.
The discriminator is breadth, not volume:
- Alert on a single principal receiving access-denied errors across more than a handful of distinct services within a short window.
- Weight
Describe*,List*, andGet*failures higher — that combination is reconnaissance, and it is what a compromised credential produces before it does anything else. - Baseline per principal. A CI role that always fails the same two calls is noise you can suppress permanently and safely.
This detection has an unusual property worth valuing: it fires during reconnaissance, before the attacker knows what they can reach. Almost every other cloud detection fires after they have already used a permission successfully.
userAgent is a cheap classifier. Console activity, SDK calls, and CLI calls are all
distinguishable, and so are some attack tools that never bothered to change their default. A
principal that has only ever appeared with an SDK user agent suddenly showing console activity is
worth a look, and the check costs nothing.
How to architect CloudTrail so detections are trustworthy
- Organization trail across all accounts and regions, so nothing escapes logging.
- Centralize to a separate security account with write-once storage the workload accounts cannot modify or delete.
- Enable GuardDuty org-wide for managed threat detection.
- Restrict who can touch trails with SCPs, and alert on any change.
- Validate by generating a benign version of each pattern (a test IAM change, a logged break-glass root login) and confirming the rule fires.
- Enable CloudTrail log file validation so tampering with delivered logs is detectable rather than merely unlikely, and confirm the security account’s bucket policy denies deletion even to its own administrators.
- Put automated response on EventBridge, not on log delivery. Trail delivery takes minutes; EventBridge rules on specific API calls fire far sooner, and that difference decides whether an automated containment action is useful or merely tidy.
The architecture point underneath all seven is that a detection is only as trustworthy as the weakest write permission on its data. If the account an attacker compromises can also modify or delete the record of what they did, every rule above becomes advisory. Separating the log destination from the workload accounts is the control that makes the rest worth building.
How do you respond to compromised AWS credentials?
There is one step here that almost everyone gets wrong the first time, and it is the step that determines whether the attacker is actually out.
- Identify the principal precisely — the role, the session name, and whether it is a human identity, an instance role, or a CI credential. The three have completely different containment paths.
- Deleting an access key does not end existing sessions. Temporary credentials issued through STS remain valid until they expire, which can be hours. An attacker who assumed a role before you deleted the key keeps working while you believe you have contained the incident. This is the step that gets missed.
- Revoke the sessions explicitly. For a role, attach a policy denying all actions where
aws:TokenIssueTimeis earlier than now — the IAM console’s revoke-sessions action does exactly this. That invalidates every credential already issued from the role, which deleting a key does not. - Then remove the underlying credential — delete the access key, disable the user, or fix the SSRF that exposed the instance role. Order matters: revoke sessions first, because removing the credential can tip the attacker off while their existing session still works.
- Enumerate everything the principal could reach, not just what it did. Its policies define the blast radius, and permissions it never exercised are still permissions it had.
- Hunt for persistence. New IAM users, new access keys on existing users, new trust policies, new Lambda functions, modified security groups, and cross-account role trusts. An attacker who held credentials for hours has almost certainly established a second path, and cleaning up the first one alone accomplishes little.
- Check whether logging was tampered with during the window, and treat any gap as the most interesting period rather than a missing record.
- Confirm what data was accessed — which is answerable only if data events were enabled on the relevant buckets before the incident, per the section above.
Step 2 and step 3 are the pair worth rehearsing. The intuitive action, deleting the key, feels like containment and is not, and the gap between the two is measured in however long an STS token has left to live.
Common CloudTrail monitoring mistakes
- Alerting on everything. Noise buries the six patterns that matter.
- Logs in the compromised account. An attacker erases them.
- Single-region trails. Attackers operate in regions you don’t watch.
- No alert on logging changes. You miss the move that blinds you.
- No data events on sensitive buckets. Object access is invisible, and the gap cannot be filled retroactively, so the “what was taken” question has no answer.
- Treating CloudTrail as real-time. Delivery takes minutes. Automated blocking belongs on EventBridge, not on log arrival.
- Missing global service events. IAM lives in
us-east-1, so a regional trail without them misses the highest-value category entirely. - CIDR-matching
sourceIPAddresswithout handling service names. The field holds*.amazonaws.comfor service-initiated calls, and the rule breaks quietly. - Alerting on role names instead of session names. In a federated organisation the role tells you the permission set, never the person.
- Ignoring
AccessDeniedbreadth. It is the only common signal that fires during reconnaissance rather than after successful use. - Deleting an access key and calling it contained. Existing STS sessions survive; revoke them by token issue time first.
CloudTrail monitoring checklist
- Enable an organization trail across all accounts and regions.
- Centralize logs to a separate, write-restricted security account.
- Alert on root usage, IAM changes, and logging/GuardDuty tampering.
- Alert on instance-role credentials used outside the VPC (exfiltration).
- Flag console logins without MFA and API calls from unusual regions.
- Enable GuardDuty org-wide; alert when it is disabled.
- Restrict trail modification with SCPs.
- Validate each detection with a benign test event.
- Enable object-level data events on crown-jewel buckets, and accept that you cannot add them retroactively.
- Extract the federated session name into its own field at ingest, so attribution is one query rather than a parsing exercise per rule.
- Alert on
AccessDeniedbreadth across services from a single principal — the one signal that fires during reconnaissance. - Rehearse session revocation by token issue time, so nobody’s first attempt at it happens during a live incident.
Items 9 and 12 are the two that are painful to add under pressure. Data events cannot be backfilled, so the decision has to be made in advance or the answer to “what was accessed” is permanently unavailable. And session revocation is a specific, slightly unintuitive IAM operation that people reach for after deleting the key has already failed to stop the activity, which is the worst moment to be reading documentation.
The takeaway
CloudTrail monitoring that matters is a short list of high-signal patterns — root usage, IAM changes, logging tampering, credential exfiltration — backed by centralized, tamper-proof storage and GuardDuty. Get those right before anything else.
Two decisions deserve making today rather than during an incident, because neither can be made retroactively. Enable object-level data events on the buckets whose contents you would have to disclose, since without them the question of what was accessed has no answer at all. And rehearse revoking sessions by token issue time, because deleting an access key feels like containment and is not — the credentials already issued from it keep working until they expire. Continue with Kubernetes security events to prioritize and SSRF detection, or watch another exfiltration channel with DNS tunneling detection. Most cloud intrusions begin with a credential that leaked rather than an exploit, so pair this with secrets detection. Or browse the full Detection Engineering pillar.
Training & tools referenced
Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.
- TryHackMeAuthorized labs to practice cloud security monitoring and detectionSecurity TrainingStart training
Frequently asked questions
What should you monitor in AWS CloudTrail?
Prioritize a small set of high-signal events: root account usage, IAM changes (new users, access keys, policy attachments), any tampering with CloudTrail or GuardDuty, console logins without MFA, and instance-role credentials used from outside your network. These catch the actions every cloud attacker takes.
How do attackers evade CloudTrail?
A common early move is disabling, deleting, or stopping a CloudTrail trail, or turning off GuardDuty, to blind defenders (MITRE T1562.008). The defense is to alert specifically on StopLogging, DeleteTrail, and detector changes, and to send logs to a separate, write-restricted account they cannot reach.
What is the difference between CloudTrail and GuardDuty?
CloudTrail is the audit log of API activity in your AWS account — the raw events. GuardDuty is a managed threat-detection service that analyzes CloudTrail, VPC flow, and DNS logs to surface findings. Use GuardDuty for managed detections and CloudTrail for your own custom, high-signal rules.
How do you detect compromised AWS credentials?
Watch for instance-role credentials used from an IP outside your VPC (exfiltration), API calls from unusual regions or new source networks, access key creation followed by privilege changes, and a burst of reconnaissance calls like ListBuckets or GetCallerIdentity from a new principal.