OAuth applications accessing user mail via GraphAPI may indicate unauthorized data exfiltration by adversaries leveraging compromised credentials, and SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential Nobelium-style espionage campaigns early. This anomaly could signal an adversary using OAuth apps to maintain persistence and access sensitive email data undetected.
KQL Query
//Look for OAuth App reading mail via GraphAPI -- that did not read mail via graph API in prior week
let appMailReadActivity = (timeframeStart:datetime, timeframeEnd:datetime) {
CloudAppEvents
| where Timestamp between (timeframeStart .. timeframeEnd)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "00000003-0000-0000-c000-000000000000" // performance check
| extend rawData = parse_json(RawEventData)
| extend AppId = tostring(parse_json(rawData.AppId))
| extend OAuthAppId = tostring(parse_json(rawData.ClientAppId)) // extract OAuthAppId
| summarize by OAuthAppId
};
appMailReadActivity(ago(1d),now()) // detection period
| join kind = leftanti appMailReadActivity(ago(7d),ago(2d)) // baseline period
on OAuthAppId
id: 010d76aa-f2e9-4b88-8134-1ae59655aafe
name: OAuth Apps reading mail via GraphAPI anomaly [Nobelium]
description: |
Use this query to review OAuth applications whose behaviour has changed as compared to a prior baseline period. The following query returns OAuth Applications accessing user mail via Graph that did not do so in the preceding week.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
tactics:
- Exfiltration
tags:
- Nobelium
query: |
//Look for OAuth App reading mail via GraphAPI -- that did not read mail via graph API in prior week
let appMailReadActivity = (timeframeStart:datetime, timeframeEnd:datetime) {
CloudAppEvents
| where Timestamp between (timeframeStart .. timeframeEnd)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "00000003-0000-0000-c000-000000000000" // performance check
| extend rawData = parse_json(RawEventData)
| extend AppId = tostring(parse_json(rawData.AppId))
| extend OAuthAppId = tostring(parse_json(rawData.ClientAppId)) // extract OAuthAppId
| summarize by OAuthAppId
};
appMailReadActivity(ago(1d),now()) // detection period
| join kind = leftanti appMailReadActivity(ago(7d),ago(2d)) // baseline period
on OAuthAppId
| Sentinel Table | Notes |
|---|---|
CloudAppEvents | Ensure this data connector is enabled |
Scenario: Scheduled Email Backup Job Using Microsoft Graph API
Description: A legitimate scheduled job runs daily to back up user emails using Microsoft Graph API via an OAuth app.
Filter/Exclusion: Exclude activities from known backup tools (e.g., Microsoft 365 Backup, Veeam Backup for Microsoft 365) or apply a time-based filter for scheduled jobs (e.g., activityDateTime >= '2024-01-01').
Scenario: User-Initiated Email Access via Power Automate
Description: A user uses Power Automate to automate a workflow that requires reading emails from their mailbox.
Filter/Exclusion: Exclude flows associated with Power Automate by checking the clientApplication field or using a custom tag like flow_id.
Scenario: Admin Task to Review Mailbox Permissions
Description: An admin uses the Microsoft 365 Admin Center or PowerShell to audit or modify mailbox permissions, which may trigger Graph API access.
Filter/Exclusion: Exclude activities from admin accounts (e.g., userPrincipalName LIKE '%admin%') or filter by known admin tools (e.g., clientApplication = 'Microsoft 365 Admin Center').
Scenario: Integration with Third-Party Email Client (e.g., Outlook for Web)
Description: A user’s email client (e.g., Outlook for Web) accesses their mailbox via Graph API as part of normal operation.
Filter/Exclusion: Exclude access from known client applications (e.g., clientApplication = 'Outlook for Web' or clientApplication = 'Microsoft Outlook').
Scenario: Automated Reporting Tool Accessing Mailbox Data
Description: A reporting tool (e.g., **Power