CLOUDTRAIL & AZURE MONITOR LOG ANALYSIS FOR SOC
A practical reference for detecting threats, investigating incidents, and building detections in AWS CloudTrail and Azure Monitor/Sentinel.
CLOUDTRAIL FUNDAMENTALS#
# Enable CloudTrail (all regions, management + data events) aws cloudtrail create-trail \ --name security-trail \ --s3-bucket-name cloudtrail-logs-bucket \ --is-multi-region-trail \ --enable-log-file-validation aws cloudtrail start-logging --name security-trail # Query CloudTrail with AWS CLI aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=ConsoleLogin # Query with time range aws cloudtrail lookup-events \ --start-time "2026-03-01T00:00:00Z" \ --end-time "2026-03-19T23:59:59Z" \ --lookup-attributes AttributeKey=EventName,AttributeValue=CreateUser # Query by username aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=Username,AttributeValue=suspicious-user # CloudTrail log structure (key fields): # eventTime, eventName, eventSource, awsRegion # sourceIPAddress, userAgent # userIdentity.type, userIdentity.arn, userIdentity.accountId # requestParameters, responseElements # errorCode, errorMessage
CRITICAL CLOUDTRAIL EVENTS TO MONITOR#
# AUTHENTICATION & ACCESS ConsoleLogin # Console sign-in (check MFA, sourceIP) ConsoleLoginFailure # Failed console login SwitchRole # User assumed another role GetSessionToken # Temporary credentials requested GetFederationToken # Federated access token created AssumeRole # Role assumption AssumeRoleWithSAML # SAML-based federation AssumeRoleWithWebIdentity # Web identity federation # USER & CREDENTIAL MANAGEMENT CreateUser # New IAM user created CreateLoginProfile # Console password set for user CreateAccessKey # New access key pair created UpdateLoginProfile # Password changed DeleteUser # User deletion (covering tracks) AddUserToGroup # Group membership change AttachUserPolicy # Direct policy attachment # POLICY & PERMISSION CHANGES CreatePolicy # New IAM policy created CreatePolicyVersion # Policy updated (check for wildcards) AttachRolePolicy # Policy attached to role PutRolePolicy # Inline policy added to role PutUserPolicy # Inline policy added to user AttachGroupPolicy # Policy attached to group # PERSISTENCE MECHANISMS CreateRole # New role created UpdateAssumeRolePolicy # Trust policy modified CreateServiceLinkedRole # Service-linked role created CreateInstanceProfile # EC2 instance profile created AddRoleToInstanceProfile # Role added to instance profile # DEFENSE EVASION StopLogging # CloudTrail logging stopped *** HIGH PRIORITY *** DeleteTrail # CloudTrail trail deleted *** HIGH PRIORITY *** UpdateTrail # Trail configuration changed PutEventSelectors # Event filtering changed DisableRule # CloudWatch/EventBridge rule disabled DeleteFlowLogs # VPC flow logs deleted DeleteDetector # GuardDuty disabled # DATA EXFILTRATION INDICATORS GetObject # S3 object downloaded (data events) PutBucketPolicy # Bucket policy changed (may allow public) PutBucketAcl # Bucket ACL changed CreateSnapshot # EBS snapshot (data theft) ModifySnapshotAttribute # Snapshot shared externally ModifyImageAttribute # AMI shared externally CopySnapshot # Cross-region/account snapshot copy AuthorizeSecurityGroupIngress # Firewall opened # RESOURCE CREATION (CRYPTOMINING / ABUSE) RunInstances # EC2 instances launched CreateFunction # Lambda function created CreateStack # CloudFormation stack deployed
ATHENA QUERIES FOR CLOUDTRAIL#
# Create Athena table for CloudTrail
CREATE EXTERNAL TABLE cloudtrail_logs (
eventversion STRING, useridentity STRUCT<type:STRING,principalid:STRING,arn:STRING,accountid:STRING>,
eventtime STRING, eventsource STRING, eventname STRING, awsregion STRING,
sourceipaddress STRING, useragent STRING, requestparameters STRING,
responseelements STRING, errorcode STRING, errormessage STRING
) ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://cloudtrail-bucket/AWSLogs/<account-id>/CloudTrail/';
# Failed console logins
SELECT eventtime, useridentity.arn, sourceipaddress, errormessage
FROM cloudtrail_logs
WHERE eventname = 'ConsoleLogin' AND errorcode = 'Failed'
ORDER BY eventtime DESC LIMIT 100;
# Access key creation
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname = 'CreateAccessKey'
ORDER BY eventtime DESC;
# CloudTrail tampering
SELECT eventtime, useridentity.arn, eventname, sourceipaddress
FROM cloudtrail_logs
WHERE eventname IN ('StopLogging', 'DeleteTrail', 'UpdateTrail', 'PutEventSelectors')
ORDER BY eventtime DESC;
# Unusual AssumeRole activity
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname = 'AssumeRole'
AND sourceipaddress NOT LIKE '10.%'
ORDER BY eventtime DESC LIMIT 200;
# S3 data exfiltration (large number of GetObject calls)
SELECT useridentity.arn, COUNT(*) as get_count, MIN(eventtime) as first_seen, MAX(eventtime) as last_seen
FROM cloudtrail_logs
WHERE eventname = 'GetObject'
GROUP BY useridentity.arn
HAVING COUNT(*) > 100
ORDER BY get_count DESC;
AZURE ACTIVITY LOG KEY EVENTS (ARM operations)#
# Subscription control plane: AzureActivity column OperationNameValue. # Case varies between records, so match with =~ or in~. More operations # and hunts: see the Azure Resource Incident Response sheet. # Subscription & Resource Microsoft.Compute/virtualMachines/write # VM created or changed Microsoft.Authorization/roleAssignments/write # Azure RBAC assignment Microsoft.Resources/subscriptions/resourceGroups/delete # RG + resources Microsoft.Network/networkSecurityGroups/write # NSG created/changed Microsoft.Network/networkSecurityGroups/securityRules/delete # rule gone # Defense Evasion Microsoft.Insights/DiagnosticSettings/Delete # logging disabled Microsoft.Insights/DiagnosticSettings/Write # logging modified # Log profiles (legacy activity log export) retired 15 Sep 2026; export # now runs only through diagnostic settings, so watch the two lines above.
ENTRA ID AUDIT LOG KEY EVENTS (AuditLogs OperationName)#
# Directory changes, not ARM. Sign-ins and risk are in other tables: SigninLogs # sign-in activity SigninLogs ResultType "500121" # MFA denied / not completed AADUserRiskEvents # ID Protection risk (P2) # More: see the Entra ID and GCP Detection sheet. # HIGH PRIORITY EVENTS # Authentication methods & consent "User registered security info" # MFA registration "Consent to application" # OAuth consent grant # Role & Permission Changes "Add member to role" # Role assignment "Add member to role completed (PIM activation)" # PIM role activation "Add eligible member to role in PIM" # PIM eligibility (use has) "Remove member from role" # Role removal # User & Group Management "Add user" # New user created "Delete user" # User deleted "Update user" # User properties changed "Add member to group" # Group membership "Add owner to group" # Group ownership "Reset password (by admin)" # Password reset "Invite external user" # Guest user invited # Application & Service Principal "Add application" # App registration "Add service principal" # Service principal created "Add service principal credentials" # Secret/cert added "Add app role assignment to service principal" # API permissions granted "Add owner to application" # App ownership changed
KQL QUERIES FOR MICROSOFT SENTINEL#
# Failed sign-ins with high volume (brute force)
SigninLogs
| where ResultType != "0"
| summarize FailedCount=count(), DistinctUsers=dcount(UserPrincipalName) by IPAddress, bin(TimeGenerated, 1h)
| where FailedCount > 20
| sort by FailedCount desc
# Successful login after multiple failures (credential stuffing success)
let FailedLogins = SigninLogs
| where ResultType != "0"
| summarize FailCount=count() by UserPrincipalName, IPAddress
| where FailCount > 5;
SigninLogs
| where ResultType == "0"
| join kind=inner FailedLogins on UserPrincipalName, IPAddress
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, FailCount
# Impossible travel detection
SigninLogs
| where ResultType == "0"
| summarize Locations=make_set(Location), LocationCount=dcount(Location) by UserPrincipalName, bin(TimeGenerated, 1h)
| where LocationCount > 1
| sort by TimeGenerated desc
# New app consent granted
AuditLogs
| where OperationName == "Consent to application"
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend AppName = tostring(TargetResources[0].displayName)
| project TimeGenerated, InitiatedBy, AppName, OperationName
# Service principal credential added
AuditLogs
| where OperationName has "Add service principal credentials"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend TargetSP = tostring(TargetResources[0].displayName)
| project TimeGenerated, Actor, TargetSP, OperationName
# Global Admin role assignment
AuditLogs
| where OperationName == "Add member to role"
# Role name sits in the target's modifiedProperties, so match the text
| where tostring(TargetResources) has "Global Administrator"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| project TimeGenerated, Actor, TargetResources
# Conditional Access policy changes
AuditLogs
| where OperationName has "conditional access policy"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend PolicyName = tostring(TargetResources[0].displayName)
| project TimeGenerated, Actor, OperationName, PolicyName
# MFA bypass attempts
SigninLogs
| where AuthenticationRequirement == "multiFactorAuthentication"
| where ResultType != "0"
| summarize Count=count() by UserPrincipalName, ResultDescription
| sort by Count desc
# Azure resource deletion (sabotage detection)
AzureActivity
| where OperationNameValue has "delete"
| where ActivityStatusValue in~ ("Success", "Succeeded")
| summarize DeleteCount=count() by Caller, bin(TimeGenerated, 1h)
| where DeleteCount > 5
| sort by DeleteCount desc
# Key Vault access anomalies
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName == "SecretGet"
| summarize Count=count() by CallerIPAddress, bin(TimeGenerated, 1h)
| where Count > 20
# Newer settings log to AZKVAuditLogs (see Azure Resource IR sheet)
# Detect diagnostic settings deletion (defense evasion)
AzureActivity
| where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE"
| project TimeGenerated, Caller, ResourceGroup, OperationNameValue
CREDENTIAL THEFT DETECTION PATTERNS#
# AWS - Access key used from unusual IP
# Baseline normal IPs for each access key, alert on new ones
SELECT useridentity.accesskeyid, sourceipaddress, COUNT(*) as call_count
FROM cloudtrail_logs
WHERE eventtime > '2026-03-12'
GROUP BY useridentity.accesskeyid, sourceipaddress;
# AWS - Root account usage (should almost never happen)
SELECT eventtime, eventname, sourceipaddress
FROM cloudtrail_logs
WHERE useridentity.type = 'Root'
ORDER BY eventtime DESC;
# Azure - Token replay detection (ID Protection "anomalousToken", P2)
AADUserRiskEvents
| where RiskEventType == "anomalousToken"
| project TimeGenerated, UserPrincipalName, IpAddress, RiskLevel
# Forged SAML from a federated issuer: RiskEventType "tokenIssuerAnomaly"
# Azure - Credential harvesting from Key Vault
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName in ("SecretGet", "SecretList", "CertificateGet")
| summarize Operations=count() by CallerIPAddress, bin(TimeGenerated, 15m)
| where Operations > 50
PERSISTENCE DETECTION PATTERNS#
# AWS - New IAM user with console access
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname IN ('CreateUser', 'CreateLoginProfile', 'CreateAccessKey')
ORDER BY eventtime DESC;
# AWS - Lambda backdoor (new function or updated code)
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname IN ('CreateFunction20150331', 'UpdateFunctionCode20150331v2')
ORDER BY eventtime DESC;
# Azure - New service principal credentials
AuditLogs
| where OperationName has_any ("Add service principal credentials", "Update application")
| where TargetResources has "KeyDescription"
| project TimeGenerated, InitiatedBy, TargetResources
# Azure - Federation trust modification
AuditLogs
| where OperationName has "Set federation settings on domain"
| project TimeGenerated, InitiatedBy, TargetResources
EXFILTRATION DETECTION PATTERNS#
# AWS - S3 bucket policy made public
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname IN ('PutBucketPolicy', 'PutBucketAcl')
ORDER BY eventtime DESC;
-- Check requestparameters for "Principal":"*" or AllUsers
# AWS - EBS snapshot shared externally
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname = 'ModifySnapshotAttribute'
ORDER BY eventtime DESC;
# AWS - Unusual data transfer volumes
# Correlate with VPC Flow Logs for bytes transferred
SELECT srcaddr, dstaddr, SUM(bytes) as total_bytes
FROM vpc_flow_logs
WHERE dstaddr NOT LIKE '10.%' AND dstaddr NOT LIKE '172.%'
GROUP BY srcaddr, dstaddr
HAVING total_bytes > 1073741824
ORDER BY total_bytes DESC;
# Azure - Mass file download from SharePoint/OneDrive
OfficeActivity
| where Operation in ("FileDownloaded", "FileSyncDownloadedFull")
| summarize DownloadCount=count(), TotalFiles=dcount(OfficeObjectId) by UserId, bin(TimeGenerated, 1h)
| where DownloadCount > 50
# Azure - Storage account key regeneration (precursor to exfil)
AzureActivity
| where OperationNameValue =~
"MICROSOFT.STORAGE/STORAGEACCOUNTS/REGENERATEKEY/ACTION"
| project TimeGenerated, Caller, ResourceGroup
SOC RESPONSE CHECKLIST#
# When suspicious CloudTrail event detected: # 1. Identify the principal (userIdentity.arn) # 2. Check sourceIPAddress against known ranges # 3. Review userAgent for unusual tools (aws-cli from unexpected locations) # 4. Correlate with GuardDuty findings # 5. Check if access key was recently created # 6. Review all actions by that principal in the time window # 7. Disable compromised credentials immediately aws iam update-access-key --access-key-id <key> --status Inactive --user-name <user> aws iam delete-login-profile --user-name <user> # When suspicious Azure sign-in detected: # 1. Check sign-in logs for the user (location, device, app) # 2. Review audit logs for post-auth activity # 3. Check risky sign-in and risky user reports # 4. Revoke sessions and reset credentials # 5. Review conditional access policy hits # 6. Check for consent grants and app registrations # Graph PowerShell (the AzureAD module is retired): # Revoke-MgUserSignInSession -UserId <user-object-id-or-UPN> # Disable the account first; steps in the Entra ID and GCP Detection sheet.
CLOUDTRAIL & AZURE MONITOR LOG ANALYSIS FOR SOC
=================================================
Source: https://cheatsheet.johlem.net
A practical reference for detecting threats, investigating incidents,
and building detections in AWS CloudTrail and Azure Monitor/Sentinel.
CLOUDTRAIL FUNDAMENTALS
------------------------
# Enable CloudTrail (all regions, management + data events)
aws cloudtrail create-trail \
--name security-trail \
--s3-bucket-name cloudtrail-logs-bucket \
--is-multi-region-trail \
--enable-log-file-validation
aws cloudtrail start-logging --name security-trail
# Query CloudTrail with AWS CLI
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=ConsoleLogin
# Query with time range
aws cloudtrail lookup-events \
--start-time "2026-03-01T00:00:00Z" \
--end-time "2026-03-19T23:59:59Z" \
--lookup-attributes AttributeKey=EventName,AttributeValue=CreateUser
# Query by username
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=suspicious-user
# CloudTrail log structure (key fields):
# eventTime, eventName, eventSource, awsRegion
# sourceIPAddress, userAgent
# userIdentity.type, userIdentity.arn, userIdentity.accountId
# requestParameters, responseElements
# errorCode, errorMessage
CRITICAL CLOUDTRAIL EVENTS TO MONITOR
---------------------------------------
# AUTHENTICATION & ACCESS
ConsoleLogin # Console sign-in (check MFA, sourceIP)
ConsoleLoginFailure # Failed console login
SwitchRole # User assumed another role
GetSessionToken # Temporary credentials requested
GetFederationToken # Federated access token created
AssumeRole # Role assumption
AssumeRoleWithSAML # SAML-based federation
AssumeRoleWithWebIdentity # Web identity federation
# USER & CREDENTIAL MANAGEMENT
CreateUser # New IAM user created
CreateLoginProfile # Console password set for user
CreateAccessKey # New access key pair created
UpdateLoginProfile # Password changed
DeleteUser # User deletion (covering tracks)
AddUserToGroup # Group membership change
AttachUserPolicy # Direct policy attachment
# POLICY & PERMISSION CHANGES
CreatePolicy # New IAM policy created
CreatePolicyVersion # Policy updated (check for wildcards)
AttachRolePolicy # Policy attached to role
PutRolePolicy # Inline policy added to role
PutUserPolicy # Inline policy added to user
AttachGroupPolicy # Policy attached to group
# PERSISTENCE MECHANISMS
CreateRole # New role created
UpdateAssumeRolePolicy # Trust policy modified
CreateServiceLinkedRole # Service-linked role created
CreateInstanceProfile # EC2 instance profile created
AddRoleToInstanceProfile # Role added to instance profile
# DEFENSE EVASION
StopLogging # CloudTrail logging stopped *** HIGH PRIORITY ***
DeleteTrail # CloudTrail trail deleted *** HIGH PRIORITY ***
UpdateTrail # Trail configuration changed
PutEventSelectors # Event filtering changed
DisableRule # CloudWatch/EventBridge rule disabled
DeleteFlowLogs # VPC flow logs deleted
DeleteDetector # GuardDuty disabled
# DATA EXFILTRATION INDICATORS
GetObject # S3 object downloaded (data events)
PutBucketPolicy # Bucket policy changed (may allow public)
PutBucketAcl # Bucket ACL changed
CreateSnapshot # EBS snapshot (data theft)
ModifySnapshotAttribute # Snapshot shared externally
ModifyImageAttribute # AMI shared externally
CopySnapshot # Cross-region/account snapshot copy
AuthorizeSecurityGroupIngress # Firewall opened
# RESOURCE CREATION (CRYPTOMINING / ABUSE)
RunInstances # EC2 instances launched
CreateFunction # Lambda function created
CreateStack # CloudFormation stack deployed
ATHENA QUERIES FOR CLOUDTRAIL
------------------------------
# Create Athena table for CloudTrail
CREATE EXTERNAL TABLE cloudtrail_logs (
eventversion STRING, useridentity STRUCT<type:STRING,principalid:STRING,arn:STRING,accountid:STRING>,
eventtime STRING, eventsource STRING, eventname STRING, awsregion STRING,
sourceipaddress STRING, useragent STRING, requestparameters STRING,
responseelements STRING, errorcode STRING, errormessage STRING
) ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://cloudtrail-bucket/AWSLogs/<account-id>/CloudTrail/';
# Failed console logins
SELECT eventtime, useridentity.arn, sourceipaddress, errormessage
FROM cloudtrail_logs
WHERE eventname = 'ConsoleLogin' AND errorcode = 'Failed'
ORDER BY eventtime DESC LIMIT 100;
# Access key creation
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname = 'CreateAccessKey'
ORDER BY eventtime DESC;
# CloudTrail tampering
SELECT eventtime, useridentity.arn, eventname, sourceipaddress
FROM cloudtrail_logs
WHERE eventname IN ('StopLogging', 'DeleteTrail', 'UpdateTrail', 'PutEventSelectors')
ORDER BY eventtime DESC;
# Unusual AssumeRole activity
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname = 'AssumeRole'
AND sourceipaddress NOT LIKE '10.%'
ORDER BY eventtime DESC LIMIT 200;
# S3 data exfiltration (large number of GetObject calls)
SELECT useridentity.arn, COUNT(*) as get_count, MIN(eventtime) as first_seen, MAX(eventtime) as last_seen
FROM cloudtrail_logs
WHERE eventname = 'GetObject'
GROUP BY useridentity.arn
HAVING COUNT(*) > 100
ORDER BY get_count DESC;
AZURE ACTIVITY LOG KEY EVENTS (ARM operations)
----------------------------------------------
# Subscription control plane: AzureActivity column OperationNameValue.
# Case varies between records, so match with =~ or in~. More operations
# and hunts: see the Azure Resource Incident Response sheet.
# Subscription & Resource
Microsoft.Compute/virtualMachines/write # VM created or changed
Microsoft.Authorization/roleAssignments/write # Azure RBAC assignment
Microsoft.Resources/subscriptions/resourceGroups/delete # RG + resources
Microsoft.Network/networkSecurityGroups/write # NSG created/changed
Microsoft.Network/networkSecurityGroups/securityRules/delete # rule gone
# Defense Evasion
Microsoft.Insights/DiagnosticSettings/Delete # logging disabled
Microsoft.Insights/DiagnosticSettings/Write # logging modified
# Log profiles (legacy activity log export) retired 15 Sep 2026; export
# now runs only through diagnostic settings, so watch the two lines above.
ENTRA ID AUDIT LOG KEY EVENTS (AuditLogs OperationName)
-------------------------------------------------------
# Directory changes, not ARM. Sign-ins and risk are in other tables:
SigninLogs # sign-in activity
SigninLogs ResultType "500121" # MFA denied / not completed
AADUserRiskEvents # ID Protection risk (P2)
# More: see the Entra ID and GCP Detection sheet.
# HIGH PRIORITY EVENTS
# Authentication methods & consent
"User registered security info" # MFA registration
"Consent to application" # OAuth consent grant
# Role & Permission Changes
"Add member to role" # Role assignment
"Add member to role completed (PIM activation)" # PIM role activation
"Add eligible member to role in PIM" # PIM eligibility (use has)
"Remove member from role" # Role removal
# User & Group Management
"Add user" # New user created
"Delete user" # User deleted
"Update user" # User properties changed
"Add member to group" # Group membership
"Add owner to group" # Group ownership
"Reset password (by admin)" # Password reset
"Invite external user" # Guest user invited
# Application & Service Principal
"Add application" # App registration
"Add service principal" # Service principal created
"Add service principal credentials" # Secret/cert added
"Add app role assignment to service principal" # API permissions granted
"Add owner to application" # App ownership changed
KQL QUERIES FOR MICROSOFT SENTINEL
------------------------------------
# Failed sign-ins with high volume (brute force)
SigninLogs
| where ResultType != "0"
| summarize FailedCount=count(), DistinctUsers=dcount(UserPrincipalName) by IPAddress, bin(TimeGenerated, 1h)
| where FailedCount > 20
| sort by FailedCount desc
# Successful login after multiple failures (credential stuffing success)
let FailedLogins = SigninLogs
| where ResultType != "0"
| summarize FailCount=count() by UserPrincipalName, IPAddress
| where FailCount > 5;
SigninLogs
| where ResultType == "0"
| join kind=inner FailedLogins on UserPrincipalName, IPAddress
| project TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, FailCount
# Impossible travel detection
SigninLogs
| where ResultType == "0"
| summarize Locations=make_set(Location), LocationCount=dcount(Location) by UserPrincipalName, bin(TimeGenerated, 1h)
| where LocationCount > 1
| sort by TimeGenerated desc
# New app consent granted
AuditLogs
| where OperationName == "Consent to application"
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend AppName = tostring(TargetResources[0].displayName)
| project TimeGenerated, InitiatedBy, AppName, OperationName
# Service principal credential added
AuditLogs
| where OperationName has "Add service principal credentials"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend TargetSP = tostring(TargetResources[0].displayName)
| project TimeGenerated, Actor, TargetSP, OperationName
# Global Admin role assignment
AuditLogs
| where OperationName == "Add member to role"
# Role name sits in the target's modifiedProperties, so match the text
| where tostring(TargetResources) has "Global Administrator"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| project TimeGenerated, Actor, TargetResources
# Conditional Access policy changes
AuditLogs
| where OperationName has "conditional access policy"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend PolicyName = tostring(TargetResources[0].displayName)
| project TimeGenerated, Actor, OperationName, PolicyName
# MFA bypass attempts
SigninLogs
| where AuthenticationRequirement == "multiFactorAuthentication"
| where ResultType != "0"
| summarize Count=count() by UserPrincipalName, ResultDescription
| sort by Count desc
# Azure resource deletion (sabotage detection)
AzureActivity
| where OperationNameValue has "delete"
| where ActivityStatusValue in~ ("Success", "Succeeded")
| summarize DeleteCount=count() by Caller, bin(TimeGenerated, 1h)
| where DeleteCount > 5
| sort by DeleteCount desc
# Key Vault access anomalies
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName == "SecretGet"
| summarize Count=count() by CallerIPAddress, bin(TimeGenerated, 1h)
| where Count > 20
# Newer settings log to AZKVAuditLogs (see Azure Resource IR sheet)
# Detect diagnostic settings deletion (defense evasion)
AzureActivity
| where OperationNameValue =~ "MICROSOFT.INSIGHTS/DIAGNOSTICSETTINGS/DELETE"
| project TimeGenerated, Caller, ResourceGroup, OperationNameValue
CREDENTIAL THEFT DETECTION PATTERNS
-------------------------------------
# AWS - Access key used from unusual IP
# Baseline normal IPs for each access key, alert on new ones
SELECT useridentity.accesskeyid, sourceipaddress, COUNT(*) as call_count
FROM cloudtrail_logs
WHERE eventtime > '2026-03-12'
GROUP BY useridentity.accesskeyid, sourceipaddress;
# AWS - Root account usage (should almost never happen)
SELECT eventtime, eventname, sourceipaddress
FROM cloudtrail_logs
WHERE useridentity.type = 'Root'
ORDER BY eventtime DESC;
# Azure - Token replay detection (ID Protection "anomalousToken", P2)
AADUserRiskEvents
| where RiskEventType == "anomalousToken"
| project TimeGenerated, UserPrincipalName, IpAddress, RiskLevel
# Forged SAML from a federated issuer: RiskEventType "tokenIssuerAnomaly"
# Azure - Credential harvesting from Key Vault
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName in ("SecretGet", "SecretList", "CertificateGet")
| summarize Operations=count() by CallerIPAddress, bin(TimeGenerated, 15m)
| where Operations > 50
PERSISTENCE DETECTION PATTERNS
-------------------------------
# AWS - New IAM user with console access
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname IN ('CreateUser', 'CreateLoginProfile', 'CreateAccessKey')
ORDER BY eventtime DESC;
# AWS - Lambda backdoor (new function or updated code)
SELECT eventtime, useridentity.arn, requestparameters, sourceipaddress
FROM cloudtrail_logs
WHERE eventname IN ('CreateFunction20150331', 'UpdateFunctionCode20150331v2')
ORDER BY eventtime DESC;
# Azure - New service principal credentials
AuditLogs
| where OperationName has_any ("Add service principal credentials", "Update application")
| where TargetResources has "KeyDescription"
| project TimeGenerated, InitiatedBy, TargetResources
# Azure - Federation trust modification
AuditLogs
| where OperationName has "Set federation settings on domain"
| project TimeGenerated, InitiatedBy, TargetResources
EXFILTRATION DETECTION PATTERNS
---------------------------------
# AWS - S3 bucket policy made public
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname IN ('PutBucketPolicy', 'PutBucketAcl')
ORDER BY eventtime DESC;
-- Check requestparameters for "Principal":"*" or AllUsers
# AWS - EBS snapshot shared externally
SELECT eventtime, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname = 'ModifySnapshotAttribute'
ORDER BY eventtime DESC;
# AWS - Unusual data transfer volumes
# Correlate with VPC Flow Logs for bytes transferred
SELECT srcaddr, dstaddr, SUM(bytes) as total_bytes
FROM vpc_flow_logs
WHERE dstaddr NOT LIKE '10.%' AND dstaddr NOT LIKE '172.%'
GROUP BY srcaddr, dstaddr
HAVING total_bytes > 1073741824
ORDER BY total_bytes DESC;
# Azure - Mass file download from SharePoint/OneDrive
OfficeActivity
| where Operation in ("FileDownloaded", "FileSyncDownloadedFull")
| summarize DownloadCount=count(), TotalFiles=dcount(OfficeObjectId) by UserId, bin(TimeGenerated, 1h)
| where DownloadCount > 50
# Azure - Storage account key regeneration (precursor to exfil)
AzureActivity
| where OperationNameValue =~
"MICROSOFT.STORAGE/STORAGEACCOUNTS/REGENERATEKEY/ACTION"
| project TimeGenerated, Caller, ResourceGroup
SOC RESPONSE CHECKLIST
-----------------------
# When suspicious CloudTrail event detected:
# 1. Identify the principal (userIdentity.arn)
# 2. Check sourceIPAddress against known ranges
# 3. Review userAgent for unusual tools (aws-cli from unexpected locations)
# 4. Correlate with GuardDuty findings
# 5. Check if access key was recently created
# 6. Review all actions by that principal in the time window
# 7. Disable compromised credentials immediately
aws iam update-access-key --access-key-id <key> --status Inactive --user-name <user>
aws iam delete-login-profile --user-name <user>
# When suspicious Azure sign-in detected:
# 1. Check sign-in logs for the user (location, device, app)
# 2. Review audit logs for post-auth activity
# 3. Check risky sign-in and risky user reports
# 4. Revoke sessions and reset credentials
# 5. Review conditional access policy hits
# 6. Check for consent grants and app registrations
# Graph PowerShell (the AzureAD module is retired):
# Revoke-MgUserSignInSession -UserId <user-object-id-or-UPN>
# Disable the account first; steps in the Entra ID and GCP Detection sheet.
Defensive reference on CyberRamen. Offensive / red-team sheets live on OffensiveRamen.com.