OAuth Apps reading mail both via GraphAPI and directly may indicate adversary use of compromised credentials to exfiltrate data, as this dual access pattern is commonly associated with Nobelium’s tactics in leveraging legitimate applications for persistent, stealthy data extraction. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential compromise of OAuth apps and prevent data exfiltration.
KQL Query
// Look for OAuth apps reading mail both via GraphAPI, and directly (not via GraphAPI)
// (one method may be legitimate and one suspect?)
let appsReadingMailDirectly = CloudAppEvents
| where Timestamp >= ago(1h)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "AppId"
| extend rawData = parse_json(RawEventData)
| extend AppId = tostring(parse_json(rawData.AppId))
| where AppId != "00000003-0000-0000-c000-000000000000"
| summarize by AppId
| project-rename OAuthAppId = AppId;
let appsReadingMailViaGraphAPI = CloudAppEvents
| where Timestamp >= ago(1h)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "ClientAppId"
| 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
| where AppId == "00000003-0000-0000-c000-000000000000"
| summarize by OAuthAppId;
// Applications reading mail both directly and via GraphAPI
// (one method may be legitimate and one suspect?)
appsReadingMailDirectly
| join kind = inner appsReadingMailViaGraphAPI
on OAuthAppId
| project OAuthAppId
id: 8ea80cde-a211-45e3-a7c3-62fae160026c
name: OAuth Apps reading mail both via GraphAPI and directly [Nobelium]
description: |
As described in previous guidance, Nobelium may re-purpose legitimate existing OAuth Applications in the environment to their own ends. However, malicious activity patterns may be discernable from legitimate ones.
The following query returns OAuth Applications that access mail both directly and via Graph, allowing review of whether such dual access methods follow expected use patterns.
Reference - https://msrc-blog.microsoft.com/2020/12/13/customer-guidance-on-recent-nation-state-cyber-attacks/
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
tactics:
- Exfiltration
tags:
- Nobelium
query: |
// Look for OAuth apps reading mail both via GraphAPI, and directly (not via GraphAPI)
// (one method may be legitimate and one suspect?)
let appsReadingMailDirectly = CloudAppEvents
| where Timestamp >= ago(1h)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "AppId"
| extend rawData = parse_json(RawEventData)
| extend AppId = tostring(parse_json(rawData.AppId))
| where AppId != "00000003-0000-0000-c000-000000000000"
| summarize by AppId
| project-rename OAuthAppId = AppId;
let appsReadingMailViaGraphAPI = CloudAppEvents
| where Timestamp >= ago(1h)
| where ActionType == "MailItemsAccessed"
| where RawEventData has "ClientAppId"
| 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
| where AppId == "00000003-0000-0000-c000-000000000000"
| summarize by OAuthAppId;
// Applications reading mail both directly and via GraphAPI
// (one method may be legitimate and one suspect?)
appsReadingMailDirectly
| join kind = inner appsReadingMailViaGraphAPI
on OAuthAppId
| project OAuthAppId
| Sentinel Table | Notes |
|---|---|
CloudAppEvents | Ensure this data connector is enabled |
Scenario: Scheduled backup job using Microsoft Graph API to sync user mail data
Filter/Exclusion: where not (process.name == "backup.exe" or process.name == "syncbackup.exe")
Rationale: Legitimate backup tools may access mail data via Graph API as part of routine operations.
Scenario: System Center Configuration Manager (SCCM) using OAuth to access mailbox data for compliance reporting
Filter/Exclusion: where not (process.name == "ConfigMgr.exe" or process.name == "SCCMReporting.exe")
Rationale: SCCM may access mailbox data for audit or compliance purposes using OAuth.
Scenario: Microsoft Teams meeting recording service accessing mailbox for storage
Filter/Exclusion: where not (process.name == "Teams.exe" or process.name == "MicrosoftTeams.exe")
Rationale: Teams may access mailbox data to store meeting recordings, which can trigger the rule.
Scenario: Exchange Online PowerShell cmdlets executed via Azure Automation for mailbox management
Filter/Exclusion: where not (process.name == "powershell.exe" and process.args contains "ExchangeOnlineManagement")
Rationale: Admins may use PowerShell to manage mailboxes, which can be flagged as suspicious activity.
Scenario: Third-party email archiving tool using OAuth to access mailbox data
Filter/Exclusion: where not (process.name == "EmailArchiver.exe" or process.name == "ArchivePro.exe")
Rationale: Email archiving tools often access mailbox data via OAuth as part of their normal operation.