The control plane is the crime scene
On-premises hunting leans on endpoints: process creation, file writes, memory. In cloud environments the most important actions never touch an endpoint you own. Someone creates an access key, assigns a role, adds a mailbox forwarding rule, snapshots a disk and shares it with another account. Each of those is an API call, and the only record is a log line.
That changes the hunter's job in three ways:
- Identity is the perimeter. Nearly every cloud intrusion starts with a valid credential or token. Hunting starts from "who is acting", not "what is running".
- Logging is a configuration choice. If it was not enabled, retained and exported before the incident, it does not exist. There is no forensic recovery.
- Volume is huge and mostly benign. Automation generates the overwhelming majority of events. Hunting is about knowing the small set of operations that matter and the baseline of who normally performs them.
If you have never run a structured hunt, start with your first threat hunt for the method. This article is the cloud-specific map.
Know your sources first
| Platform | Log | What it covers | Watch out for |
|---|---|---|---|
| AWS | CloudTrail management events | Control plane API calls in every service | Event history is 90 days and per region; create a multi-region trail to S3 for real retention |
| AWS | CloudTrail data events | S3 object access, Lambda invoke, etc. | Not enabled by default, extra cost, enable selectively on sensitive buckets |
| Azure | Activity Log | Azure Resource Manager operations (create, delete, role assignments) | 90 days in the portal; export via diagnostic settings |
| Entra ID | Sign-in logs | Interactive, non-interactive, service principal, managed identity sign-ins | Non-interactive logs are separate and high volume; you need them |
| Entra ID | Audit logs | Directory changes: users, groups, roles, apps, consents | Retention depends on licence; export to a SIEM |
| M365 | Unified Audit Log | Exchange, SharePoint, OneDrive, Teams, admin actions | Retention depends on licence tier; some events need Audit Premium |
Check retention before you need it. Many cloud intrusions are discovered weeks or months after initial access. If your logs only live in the native console with default retention, the start of the story may already be gone. Export to storage you control.
AWS CloudTrail
High-value events
Group them by what the attacker is trying to achieve:
| Goal | Event names |
|---|---|
| Recon after credential theft | GetCallerIdentity, ListUsers, ListRoles, ListBuckets, GetAccountAuthorizationDetails |
| Persistence | CreateUser, CreateAccessKey, CreateLoginProfile, UpdateLoginProfile, UpdateAssumeRolePolicy |
| Privilege escalation | AttachUserPolicy, PutUserPolicy, AttachRolePolicy, CreatePolicyVersion, PassRole via RunInstances or CreateFunction |
| Defense evasion | StopLogging, DeleteTrail, UpdateTrail, PutEventSelectors, GuardDuty DeleteDetector, UpdateDetector |
| Exfiltration | PutBucketPolicy, DeletePublicAccessBlock, ModifySnapshotAttribute, ModifyDBSnapshotAttribute, GetSecretValue bursts |
| Impact and abuse | RunInstances in regions you never use (cryptomining), CreateKeyPair, AuthorizeSecurityGroupIngress to 0.0.0.0/0 |
GetCallerIdentity deserves a mention: it is the "whoami" of AWS, and almost every tool runs it first. On its own it means nothing. From a new IP, for a long-lived access key that normally only runs from your CI, it is an excellent first clue.
Example queries (Athena over CloudTrail in S3)
-- Anyone tampering with logging
SELECT eventtime, eventname, useridentity.arn, sourceipaddress, awsregion
FROM cloudtrail_logs
WHERE eventname IN ('StopLogging','DeleteTrail','UpdateTrail','PutEventSelectors','DeleteDetector')
AND eventtime > to_iso8601(current_timestamp - interval '30' day)
ORDER BY eventtime DESC;
-- Access keys used from IPs never seen for that key in the prior 30 days
WITH recent AS (
SELECT useridentity.accesskeyid AS key, sourceipaddress AS ip, min(eventtime) AS first_seen
FROM cloudtrail_logs
WHERE eventtime > to_iso8601(current_timestamp - interval '1' day)
AND useridentity.accesskeyid LIKE 'AKIA%' -- long-term IAM user keys
GROUP BY 1, 2
),
history AS (
SELECT DISTINCT useridentity.accesskeyid AS key, sourceipaddress AS ip
FROM cloudtrail_logs
WHERE eventtime BETWEEN to_iso8601(current_timestamp - interval '31' day)
AND to_iso8601(current_timestamp - interval '1' day)
)
SELECT r.* FROM recent r
LEFT JOIN history h ON r.key = h.key AND r.ip = h.ip
WHERE h.key IS NULL;
-- Failed API calls by principal: enumeration usually trips AccessDenied
SELECT useridentity.arn, count(*) AS denied, count(DISTINCT eventname) AS distinct_calls
FROM cloudtrail_logs
WHERE errorcode IN ('AccessDenied','Client.UnauthorizedOperation')
AND eventtime > to_iso8601(current_timestamp - interval '1' day)
GROUP BY 1
HAVING count(DISTINCT eventname) > 15
ORDER BY distinct_calls DESC;
The last one is a favourite: legitimate automation fails on the same one or two calls repeatedly; an attacker exploring permissions fails on many different calls.
Azure and Entra ID
Sign-in logs
Most Azure and M365 intrusions begin here. Useful dimensions: ResultType (0 is success, 50126 invalid credentials, 50053 locked account, 500121 strong authentication failed), AuthenticationProtocol, ClientAppUsed, IPAddress, AutonomousSystemNumber, ConditionalAccessStatus.
// Password spray: many users, few attempts each, from one source
SigninLogs
| where TimeGenerated > ago(1d) and ResultType == "50126"
| summarize Users = dcount(UserPrincipalName), Attempts = count() by IPAddress
| where Users > 20 and Attempts / Users < 3
// Successful sign-ins via legacy protocols (no MFA possible)
SigninLogs
| where ResultType == "0"
| where ClientAppUsed in ("IMAP4", "POP3", "Authenticated SMTP", "Exchange ActiveSync", "Other clients")
| project TimeGenerated, UserPrincipalName, ClientAppUsed, IPAddress
Do not forget AADNonInteractiveUserSignInLogs (refresh token use, where replayed tokens show up) and AADServicePrincipalSignInLogs (workload identities, increasingly the target).
Audit logs
High-value operations in Entra audit logs:
Add member to roleand eligible or active PIM assignments, especially Global Administrator and Privileged Role Administrator.Add service principal credentialsandUpdate application – Certificates and secrets management: a secret added to an existing app is a classic, quiet backdoor.Consent to applicationandAdd delegated permission grant: covered in depth in our identity attacks article.Set federation settings on domainandSet domain authentication: federation backdoors let an attacker mint tokens for any user.User registered security info: a new MFA method right after a suspicious sign-in.
AuditLogs
| where OperationName in ("Add service principal credentials",
"Update application – Certificates and secrets management",
"Add member to role", "Set federation settings on domain")
| extend Actor = coalesce(tostring(InitiatedBy.user.userPrincipalName),
tostring(InitiatedBy.app.displayName))
| project TimeGenerated, OperationName, Actor, Target = tostring(TargetResources[0].displayName)
Azure Activity Log
For the resource plane, watch Microsoft.Authorization/roleAssignments/write (who got Owner on what), Microsoft.Compute/virtualMachines/runCommand/action (code execution on VMs from the control plane), Microsoft.Compute/disks/beginGetAccess/action (generating a download URL for a disk, a strong exfiltration signal) and changes to diagnostic settings that disable logging.
Microsoft 365 Unified Audit Log
Once an attacker owns a mailbox or a SharePoint identity, this is where their actions appear.
| Operation | Why it matters |
|---|---|
New-InboxRule, Set-InboxRule, UpdateInboxRules |
Hide replies, auto-delete security alerts, forward invoices. BEC hallmark |
Set-Mailbox with forwarding parameters |
Silent external forwarding |
Add-MailboxPermission, Add-RecipientPermission |
Access to other mailboxes |
New-TransportRule |
Tenant-wide mail manipulation, admin-level |
MailItemsAccessed |
Which mail items a session actually read, key for breach scoping |
FileDownloaded, FileSyncDownloadedFull |
Bulk SharePoint and OneDrive exfiltration |
New-ComplianceSearch, New-ComplianceSearchAction |
Attackers with admin rights use eDiscovery to search and export everything |
# Exchange Online PowerShell: inbox rules created in the last 7 days
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-Operations New-InboxRule,Set-InboxRule,UpdateInboxRules -ResultSize 5000 |
Select-Object CreationDate, UserIds, Operations, AuditData
Inside AuditData, look at the rule parameters. Rules that move messages containing words like "invoice", "payment", "phishing", "hacked" to RSS Feeds or Archive, or that delete them, are rarely created by humans for themselves.
Connecting the dots
Single events are leads. Sequences are findings. The patterns worth turning into hunts:
1. Sign-in from new ASN -> new MFA method registered -> inbox rule created (mailbox takeover)
2. Access key from new IP -> GetCallerIdentity -> ListBuckets -> GetObject burst (credential leak to data theft)
3. App credential added -> service principal sign-in from new IP -> Graph mail reads (app backdoor)
4. Role assignment to user -> diagnostic settings deleted -> runCommand on VMs (escalation and evasion)
Enrich the IPs and domains along the way: an IP from a hosting provider or anonymising service deserves a different priority than one from the user's home ISP. Look them up on /search and record the pivots in a case so the reasoning survives the shift change.
Want to know what these events really look like in your tenant before an attacker generates them? The M365 red kit helps you build controlled scenarios around Microsoft 365 so you can validate that each query above actually returns something.
What to do Monday
- [ ] Confirm a multi-region CloudTrail trail exists, writes to a separate account's S3 bucket, and has log file validation on.
- [ ] Export Entra sign-in (all four types) and audit logs, and the Azure Activity Log, to your SIEM or storage you control.
- [ ] Check your Unified Audit Log retention and whether
MailItemsAccessedis available on your licence. - [ ] Turn the logging-tampering query into an alert today. There is no legitimate reason for it to fire unannounced.
- [ ] Baseline who normally creates access keys, app secrets and role assignments; hunt for everyone else.
- [ ] Pick one sequence from the list above and run it as this month's hypothesis-driven hunt.
Cloud attackers leave no files behind, but they cannot act without calling an API. Every call is a line in a log. Make sure you are keeping it, then go read it.