Skip to main content

Microsoft Telemetry to UDM Mapping: Part 3 - Cloud Detection & Cross-Source Correlation

  • June 1, 2026
  • 0 replies
  • 33 views

Digital-Customer-Excellence
Staff
Forum|alt.badge.img+7

Author:

David Nehoda, Technical Solutions Consultant

 

Entra ID, Office 365, Defender, and End-to-End Attack Chains

---


Overview

This section covers cloud Microsoft infrastructure: Entra ID (Azure AD), Office 365 (Exchange, SharePoint, Teams), and Defender for Endpoint. Combined with on-premises data, these sources enable end-to-end attack chain detection from phishing through Kerberos abuse to data exfiltration.

Section 1: Entra ID (Azure AD)

Entra ID is not the same source as on-prem AD. Different schema, different field names, different UDM mappings. Most teams trip here because they assume on-prem rules work on cloud logs. They do not.

Key Log Sources
 

Source

Description

Priority

Entra ID Sign-in logs

Interactive, non-interactive, service principal sign-ins

Critical

Entra ID Audit logs

Directory changes, role assignments, application consent

Critical

Risky sign-ins

Microsoft Identity Protection verdicts (anonymous IP, unfamiliar location, leaked creds)

High

Risky users

Users flagged as at-risk

High

Provisioning logs

SCIM provisioning from/to Entra ID

Medium


Critical Operations and Events
 

Operation

UDM Event Type

Threat

Add application

USER_RESOURCE_CREATION

Rogue app registration for consent phishing

Consent to application

USER_RESOURCE_UPDATE_PERMISSIONS

Illicit consent grant, cloud equivalent of admin shell

Add OAuth2PermissionGrant

USER_RESOURCE_UPDATE_PERMISSIONS

Granting Graph API scopes to attacker-controlled app

Add member to role

GROUP_MODIFICATION

Granting Global Admin, Privileged Role Admin

Update user (sensitive fields)

USER_RESOURCE_UPDATE_CONTENT

Attacker modifying MFA, authentication methods

Add authentication method

USER_CHANGE_PASSWORD

Attacker enrolling new MFA device

Disable account or Delete user

USER_DELETION

Anti-forensics, denial of service against defenders

Risky sign-in

USER_LOGIN

Anonymous IP, atypical travel—account takeover indicators

Sign-in with CA Failure

USER_LOGIN

MFA prompts, location blocks, compliant device blocks


Field Mapping: Entra ID
 

Microsoft Field

UDM Field

Notes

userPrincipalName

principal.user.email_addresses or principal.user.userid

Cloud identity

ipAddress (sign-in)

principal.ip

Source IP

location.city, location.countryOrRegion

principal.location.city, principal.location.country_or_region

For impossible travel detection

appDisplayName

target.application

What app the user signed in to

resourceDisplayName

target.resource.name

Resource accessed

clientAppUsed

network.http.user_agent

Browser, rich client, mobile app, legacy auth

riskLevel

security_result.risk_score or security_result.detection_fields[RiskLevel]

Identity Protection verdict

conditionalAccessStatus

security_result.action (ALLOW, BLOCK, CHALLENGE)

CA policy result

authenticationDetails

extensions.auth.auth_details

MFA, password, certificate, FIDO2

initiatedBy.user.userPrincipalName

principal.user.email_addresses

For audit logs, who did it

targetResources[0].userPrincipalName

target.user.email_addresses

For audit logs, who got changed


YARA-L Rules: Entra ID
 

#### Rule 1: Illicit OAuth Consent Grant (Consent Phishing)

The attack Microsoft's own docs underplay. User clicks link, grants `offline_access` and `Mail.Read` to attacker's app, attacker reads mail forever without knowing password.
 

rule Detect_Illicit_OAuth_Consent_Grant {
meta:
author = "Detection Engineering"
description = "Consent to application or OAuth2 permission grant that includes high-risk scopes (Mail.Read, Files.Read.All, offline_access). Filter against known-good app allowlist."
severity = "HIGH"
mitre_attack = "T1528"

events:
$consent.metadata.log_type = "AZURE_AD"
($consent.metadata.product_event_type = "Consent to application" or
$consent.metadata.product_event_type = "Add OAuth2PermissionGrant" or
$consent.metadata.product_event_type = "Add delegated permission grant")
re.regex($consent.security_result.detection_fields["Scope"], `(?i).*(Mail\.Read|Files\.Read\.All|Sites\.Read\.All|offline_access|Mail\.Send|full_access_as_user).*`)
not $consent.target.application in %approved_oauth_apps
$consent.principal.user.email_addresses = $user

match:
$user over 1h

outcome:
$scopes = array_distinct($consent.security_result.detection_fields["Scope"])
$apps = array_distinct($consent.target.application)

condition:
$consent
}


#### Rule 2: Impossible Travel
 

rule Detect_Impossible_Travel_EntraID {
meta:
author = "Detection Engineering"
description = "Two successful sign-ins for same user from distinct countries within 2 hours. Filters out known VPN exit IPs and service principals."
severity = "HIGH"
mitre_attack = "T1078.004"

events:
$s1.metadata.log_type = "AZURE_AD"
$s1.metadata.event_type = "USER_LOGIN"
$s1.security_result.action = "ALLOW"
$s1.principal.user.email_addresses = $user
$s1.principal.location.country_or_region = $c1

$s2.metadata.log_type = "AZURE_AD"
$s2.metadata.event_type = "USER_LOGIN"
$s2.security_result.action = "ALLOW"
$s2.principal.user.email_addresses = $user
$s2.principal.location.country_or_region = $c2

$c1 != $c2
$s2.metadata.event_timestamp.seconds > $s1.metadata.event_timestamp.seconds
$s2.metadata.event_timestamp.seconds < $s1.metadata.event_timestamp.seconds + 7200

match:
$user over 2h

outcome:
$countries = array_distinct($c1)
$countries_second = array_distinct($c2)

condition:
$s1 and $s2
}


#### Rule 3: MFA Fatigue (Push Bombing)
 

rule Detect_MFA_Push_Bombing {
meta:
author = "Detection Engineering"
description = "Burst of MFA challenges for one user, followed by successful auth. Pattern of Nobelium, Lapsus$, and most modern AiTM phishing kits."
severity = "HIGH"
mitre_attack = "T1621"

events:
$fail.metadata.log_type = "AZURE_AD"
$fail.metadata.event_type = "USER_LOGIN"
$fail.security_result.action = "BLOCK"
re.regex($fail.security_result.detection_fields["StatusReason"], `(?i).*(MFA|authentication failed|user declined).*`)
$fail.principal.user.email_addresses = $user

match:
$user over 10m

outcome:
$fail_count = count($fail)

condition:
$fail and $fail_count >= 5
}


Pair this with the second rule correlating MFA burst to subsequent successful sign-in for true push-bombing confirmation.

 

Section 2: Office 365 and Exchange Online

The Management Activity API is where Business Email Compromise, insider exfiltration, and consent phishing live. The operations to collect are not the ones Microsoft's default compliance policies enable.
 

Critical O365 Operations
 

Operation

UDM Event Type

Threat

Priority

MailboxLogin (non-owner)

USER_LOGIN

Delegate or admin abuse, MFA bypass

HIGH

New-InboxRule

EMAIL_TRANSACTION or USER_RESOURCE_UPDATE_CONTENT

BEC concealment, auto-forward, auto-delete alerts

CRITICAL

Set-InboxRule

USER_RESOURCE_UPDATE_CONTENT

Modifying existing rules

HIGH

Add-MailboxPermission

USER_RESOURCE_UPDATE_PERMISSIONS

Persistent delegate access for monitoring

CRITICAL

Set-Mailbox with ForwardingSmtpAddress

USER_RESOURCE_UPDATE_CONTENT

Transport-level forwarding bypasses inbox rules

CRITICAL

FileDownloaded

NETWORK_HTTP

Mass exfiltration from SharePoint or OneDrive

HIGH

FileShared

USER_RESOURCE_UPDATE_PERMISSIONS

Sharing sensitive files externally

HIGH

AnonymousLinkCreated

USER_RESOURCE_UPDATE_PERMISSIONS

Public link creation for internal docs

CRITICAL

UserLoggedIn (unusual location)

USER_LOGIN

Account takeover indicator

HIGH

Add-RoleGroupMember

GROUP_MODIFICATION

Granting Exchange admin

CRITICAL

Set-AdminAuditLogConfig

SETTING_MODIFICATION

Disabling admin audit logging, anti-forensics

CRITICAL

New-ComplianceSearch

USER_RESOURCE_CREATION

eDiscovery abuse, content collection by attacker

CRITICAL

New-ComplianceSearchAction (Export)

USER_RESOURCE_UPDATE_CONTENT

eDiscovery export—data leaves tenant

CRITICAL

Add-MailboxFolderPermission (Owner)

USER_RESOURCE_UPDATE_PERMISSIONS

Mailbox folder takeover

HIGH


Field Mapping: O365
 

Microsoft Field

UDM Field

Notes

Operation

metadata.product_event_type

Operation name verbatim

UserId

principal.user.email_addresses

Account performing the action

ClientIP

principal.ip

Source IP (sometimes Exchange service IP for admin commands)

Parameters.ForwardTo or Parameters.RedirectTo

security_result.detection_fields[ForwardTo]

External forwarding target

Parameters.Name

target.resource.name

Rule or resource name

SiteUrl

target.url

SharePoint or OneDrive site

SourceFileName

target.file.full_path

Downloaded or shared file

ObjectId

target.resource.id

Mailbox or resource identifier

RecordType

metadata.product_log_id

1=Azure AD, 2=Exchange Admin, 6=SharePoint File, etc.


YARA-L Rules: Office 365

#### Rule 4: External Forwarding Inbox Rule (BEC Fingerprint)
 

rule Detect_O365_External_Forwarding_Inbox_Rule {
meta:
author = "Detection Engineering"
description = "New or modified inbox rule that forwards/redirects to external address, or moves finance/security keyword messages to Deleted Items. Classic BEC concealment."
severity = "CRITICAL"
mitre_attack = "T1114.003"

events:
$o365.metadata.log_type = "O365"
($o365.metadata.product_event_type = "New-InboxRule" or
$o365.metadata.product_event_type = "Set-InboxRule" or
$o365.metadata.product_event_type = "Set-Mailbox")
(re.regex($o365.security_result.detection_fields["ForwardTo"], `(?i)@(gmail|proton|yahoo|hotmail|outlook|icloud|yandex|mail\.ru|aol|tutanota)\.`) or
re.regex($o365.security_result.detection_fields["RedirectTo"], `(?i)@(gmail|proton|yahoo|hotmail|outlook|icloud|yandex|mail\.ru|aol|tutanota)\.`) or
re.regex($o365.security_result.detection_fields["ForwardingSmtpAddress"], `(?i)smtp:.*@.*`) or
(re.regex($o365.target.resource.name, `(?i).*(invoice|payment|wire|transfer|security|login|alert|suspic).*`) and
re.regex($o365.security_result.detection_fields["MoveToFolder"], `(?i).*(Junk|RSS|Deleted|Archive|Conversation History).*`)))
$o365.principal.user.email_addresses = $victim

match:
$victim over 1h

outcome:
$rule_names = array_distinct($o365.target.resource.name)
$forward_targets = array_distinct($o365.security_result.detection_fields["ForwardTo"])

condition:
$o365
}


#### Rule 5: Mass SharePoint/OneDrive Download
 

rule Detect_Mass_SharePoint_Download {
meta:
author = "Detection Engineering"
description = "One user downloading 100+ files from SharePoint/OneDrive in 10-minute window. Catches insider exfiltration and account takeover data pulls."
severity = "HIGH"
mitre_attack = "T1530"

events:
$dl.metadata.log_type = "O365"
$dl.metadata.product_event_type = "FileDownloaded"
$dl.principal.user.email_addresses = $user

match:
$user over 10m

outcome:
$file_count = count($dl)
$unique_files = count_distinct($dl.target.file.full_path)
$sites = array_distinct($dl.target.url)

condition:
$dl and $file_count > 100
}


#### Rule 6: eDiscovery Export Abuse

Attackers who obtain Compliance Administrator credentials use eDiscovery to quietly search and export mailbox content. One of the highest-impact post-compromise TTPs with almost no detection.
 

rule Detect_eDiscovery_Export_Activity {
meta:
author = "Detection Engineering"
description = "Compliance search export/preview by account not on approved eDiscovery operator list."
severity = "CRITICAL"
mitre_attack = "T1213"

events:
$ed.metadata.log_type = "O365"
($ed.metadata.product_event_type = "New-ComplianceSearchAction" or
$ed.metadata.product_event_type = "Start-ComplianceSearch" or
$ed.metadata.product_event_type = "New-ComplianceSearch")
not $ed.principal.user.email_addresses in %approved_ediscovery_operators
$ed.principal.user.email_addresses = $actor

match:
$actor over 1h

outcome:
$searches = array_distinct($ed.target.resource.name)
$operations = array_distinct($ed.metadata.product_event_type)

condition:
$ed
}


Section 3: Microsoft Defender for Endpoint

Defender generates two telemetry streams: alerts (high signal, pre-triaged) and raw device events (Sysmon-equivalent, high volume). Ingest both only if sure you need them—raw stream duplicates Sysmon at 80% overlap.
 

Alert Stream Field Mapping
 

Microsoft Field

UDM Field

Notes

Title

security_result.summary

Alert title

Severity

security_result.severity

UDM enum: LOW, MEDIUM, HIGH, CRITICAL

Category

security_result.category_details

Defender threat category

AlertId

metadata.product_log_id

Unique ID for correlation

DetectionSource

security_result.detection_fields[DetectionSource]

EDR, AV, SmartScreen, Automated Investigation

MachineId

target.asset_id

Defender-specific device identifier

MachineName or ComputerDnsName

target.hostname

Affected endpoint

Sha256 (evidence)

target.file.sha256

File hash from alert

AccountName or AccountUpn

target.user.userid or target.user.email_addresses

Affected user

RelatedUser

target.user.userid

Distinct from AccountName in some alerts

RemediationAction

security_result.action_details

What Defender did (quarantine, block, allow)


YARA-L Rules: Defender

#### Rule 7: Defender Alert Followed by Outbound Connection

The C-suite question: did malware beacon before Defender killed it?
 

rule Correlate_Defender_Alert_With_Post_Alert_Beacon {
meta:
author = "Detection Engineering"
description = "Defender alert on a host followed by successful outbound connection within 5 minutes. Beacon may be attacker traffic that succeeded before containment."
severity = "CRITICAL"
mitre_attack = "T1071"

events:
$def.metadata.log_type = "WINDOWS_DEFENDER_ATP"
$def.metadata.event_type = "SECURITY_ALERT"
$def.target.hostname = $host

$net.metadata.event_type = "NETWORK_CONNECTION"
$net.security_result.action = "ALLOW"
$net.principal.hostname = $host

$net.metadata.event_timestamp.seconds >= $def.metadata.event_timestamp.seconds
$net.metadata.event_timestamp.seconds <= $def.metadata.event_timestamp.seconds + 300

not $net.target.ip in %known_corporate_ranges

match:
$host over 10m

outcome:
$c2_destinations = array_distinct($net.target.ip)
$alert_summary = array_distinct($def.security_result.summary)

condition:
$def and $net
}


#### Rule 8: Defender Tampering Attempt
 

rule Detect_Defender_Tampering_Attempt {
meta:
author = "Detection Engineering"
description = "Commands, registry changes, services targeting Defender components. Covers Set-MpPreference exclusions, service stops, policy registry writes."
severity = "HIGH"
mitre_attack = "T1562.001"

events:
$tamper.principal.hostname = $host
(($tamper.metadata.event_type = "PROCESS_LAUNCH" and
(re.regex($tamper.target.process.command_line, `(?i).*Set-MpPreference.*(-Disable|ExclusionPath|ExclusionProcess|MAPSReporting\s+0|SubmitSamplesConsent\s+0).*`) or
re.regex($tamper.target.process.command_line, `(?i).*(sc\s+(stop|delete|config)\s+(WinDefend|Sense|WdNisSvc)).*`) or
re.regex($tamper.target.process.command_line, `(?i).*(Add-MpPreference\s+-ExclusionPath).*`) or
re.regex($tamper.target.process.command_line, `(?i).*(DisableAntiSpyware|DisableRealtimeMonitoring|DisableBehaviorMonitoring).*`))) or
($tamper.metadata.event_type = "REGISTRY_MODIFICATION" and
re.regex($tamper.target.registry.registry_key, `(?i).*\\Microsoft\\Windows Defender\\(Real-Time Protection|Exclusions|Policy Manager).*`)))

match:
$host over 10m

outcome:
$actions = array_distinct($tamper.target.process.command_line)
$reg_keys = array_distinct($tamper.target.registry.registry_key)

condition:
$tamper
}


Section 4: Reference Lists and Data Tables

Every rule in this guide assumes you maintain these. A rule without context joins is a rule that storms the SOC. A rule with reference-list exclusions fires only on the real thing.
 

Reference Lists to Build
 

Name

Type

Purpose

Seed With

`approved_oauth_apps`

STRING

Entra ID app display names that have been vetted

Output of `Get-MgServicePrincipal` filtered by publisher

`approved_ediscovery_operators`

STRING

UPNs that legitimately run eDiscovery

Your eDiscovery team roster

`known_corporate_ranges`

CIDR

IP ranges of offices and VPN exits

Network team CMDB

`high_risk_geo_countries`

STRING

ISO country codes to flag in sign-in rules

Your threat model—sanctioned countries + no-business locations


Data Tables for Multi-Column Lookups

When a reference list is not enough, use a data table. Example: service accounts with expected source hosts.

| sam_account_name      | allowed_source_host    |

|---------------------------------------|---------------------------------------|

| svc_sql_prod                   | sqlprod-01.corp.local  |

| svc_sql_prod                   | sqlprod-02.corp.local  |

| svc_backup_daily           | backup-01.corp.local   |

 

A YARA-L rule can then detect "service account logged in from host not in its approved list" without hardcoding entire mapping inside the rule.

 

Section 5: The Cross-Source Attack Chain Rule

The rule the executive summary of every detection engineering proposal promises and almost none deliver. Correlates O365 phishing sign-in, Sysmon PowerShell on same user's workstation, Kerberos ticket anomaly—all keyed off user identity.
 

rule Detect_Cross_Source_Phish_to_Kerberos_Abuse {
meta:
author = "Detection Engineering"
description = "End-to-end attack chain: risky O365 sign-in → suspicious PowerShell on user's workstation → Kerberos request with RC4. Any one stage is noisy. All three in sequence within 4 hours is a real incident."
severity = "CRITICAL"
mitre_attack = "T1566.002, T1059.001, T1558.003"

events:
$phish.metadata.log_type = "O365"
$phish.metadata.event_type = "USER_LOGIN"
$phish.security_result.action = "ALLOW"
(re.regex($phish.security_result.detection_fields["RiskLevel"], `(?i)(high|medium)`) or
re.regex($phish.principal.ip, `.*`))
$phish.principal.user.email_addresses = $user_email

$ps.metadata.event_type = "PROCESS_LAUNCH"
re.regex($ps.target.process.file.full_path, `(?i).*\\powershell(_ise)?\.exe$`)
re.regex($ps.target.process.command_line, `(?i).*(-enc|FromBase64String|DownloadString|IEX|Invoke-Expression|bypass|-nop|-w\s+hidden).*`)
$ps.target.user.userid = $user_id

$krb.metadata.log_type = "WINEVTLOG"
($krb.metadata.product_event_type = "4768" or $krb.metadata.product_event_type = "4769")
$krb.security_result.detection_fields["TicketEncryptionType"] = "0x17"
$krb.principal.user.userid = $user_id

$ps.metadata.event_timestamp.seconds >= $phish.metadata.event_timestamp.seconds
$ps.metadata.event_timestamp.seconds <= $phish.metadata.event_timestamp.seconds + 14400
$krb.metadata.event_timestamp.seconds >= $ps.metadata.event_timestamp.seconds
$krb.metadata.event_timestamp.seconds <= $ps.metadata.event_timestamp.seconds + 14400

match:
$user_id over 4h

outcome:
$sign_in_ip = array_distinct($phish.principal.ip)
$powershell_cmd = array_distinct($ps.target.process.command_line)
$kerberos_host = array_distinct($krb.principal.hostname)
$kerberos_targets = array_distinct($krb.target.application)

condition:
$phish and $ps and $krb
}

 

Note: Mapping `user_email` to `user_id` requires that on-prem UPN matches O365 UPN. If not, use a data table to resolve.
 

Section 6: Validating Rules Before Shipping

The YARA-L Validator MCP generates synthetic UDM events from a rule's trigger conditions, ingests them into Chronicle, and confirms the rule fires.

**Workflow:**

1. Write the rule

2. Paste into validator

3. Validator extracts UDM fields the rule reads, generates events satisfying them, ingests

4. Validator polls Chronicle until rule fires or times out

5. Optional: generate perturbations to prove rule doesn't fire on benign traffic

**Cache the passing events as a fixture.** Every future rule edit is regression-tested against the same fixture.

**What the Validator Cannot Do:**

Composite rules (rules chaining detections from other rules) cannot be fast-validated. Chronicle evaluates composites on HOURLY or DAILY schedules. Use validator overnight.

 

Section 7: Conclusion

Microsoft's telemetry is not broken. It is fragmented by design. What breaks is the detection logic depending on those schemas.

Google SecOps with strict UDM mapping solves this. One field path (`principal.user.userid`) matches on-prem AD, Entra ID, O365, and Defender. One event type (`PROCESS_LAUNCH`) matches Sysmon and Security 4688. One rule tracks an adversary from phishing link to forged Kerberos ticket without a single vendor-specific reference.

**The rules in this guide fire today. They will still fire after Microsoft's next schema revision, because UDM absorbs the change at the parser layer.**
 

Key Takeaways

✅ Entra ID is not on-prem AD—different field names, different event types

✅ O365 is the primary vector for BEC, eDiscovery abuse, and persistence

✅ Defender catches some, misses others—YARA-L rules are your second layer

✅ Cross-source rules detect end-to-end attacks, not individual events

✅ Reference lists reduce false positives 100x over

✅ Validate rules before shipping, not after they go noisy


**Deploy with confidence. These rules survive Microsoft's next update.**