Query Details

RULE 11 M365 DLP High Volume Sensitive Match

Query

// Rule    : M365 - DLP Policy Match - High Volume or Sensitive Data Type
// Severity: High
// Tactics : Exfiltration, Collection
// MITRE   : T1537, T1567.002
// Freq    : PT1H   Period: PT1H
// Description: Alerts when the Microsoft 365 DLP engine generates a significant
//              number of policy matches, or when a match involves high-sensitivity
//              data types (Credit Cards, SSNs, Passports, Health Records).
//              This rule focuses on anomalous user-level DLP match spikes.
//==========================================================================================

let HighSensitivityTypes = dynamic([
    "Credit Card Number", "U.S. Social Security Number",
    "International Banking Account Number", "Passport Number",
    "U.S. Driver's License Number", "EU Debit Card Number",
    "U.S. Individual Taxpayer Identification Number",
    "U.S. / U.K. Passport Number", "Medical Terms"
]);
let DLPThreshold   = 20;    // DLP matches per user per hour
let LookbackPeriod = 1h;

OfficeActivity
| where TimeGenerated > ago(LookbackPeriod)
| where RecordType == "ComplianceDLPExchange"
    or RecordType == "ComplianceDLPSharePoint"
    or RecordType == "ComplianceDLPSharePointClassification"
| extend
    PolicyName     = tostring(parse_json(PolicyDetails)[0].PolicyName),
    SensitiveTypes = tostring(parse_json(PolicyDetails)[0].Rules[0].ConditionsMatched.SensitiveInformation),
    DLPAction      = tostring(parse_json(PolicyDetails)[0].Rules[0].Actions)
| extend
    IsHighSensitivity = SensitiveTypes has_any (HighSensitivityTypes),
    IsBlocked         = DLPAction has_any ("BlockAccess", "NotifyUser", "BlockWithOverride")
| summarize
    MatchCount        = count(),
    Policies          = make_set(PolicyName, 10),
    HighSensCount     = countif(IsHighSensitivity),
    BlockedCount      = countif(IsBlocked),
    UniqueFilesSites  = make_set(ObjectId, 10),
    ClientIPs         = make_set(ClientIP, 5)
    by UserId
| where MatchCount >= DLPThreshold or HighSensCount >= 3
| extend AlertSeverity = case(
    HighSensCount >= 5,    "Critical",
    HighSensCount >= 1,    "High",
    MatchCount >= 50,      "High",
    "Medium")
| project
    TimeGenerated  = now(),
    UserId,
    MatchCount,
    HighSensCount,
    BlockedCount,
    Policies,
    UniqueFilesSites,
    ClientIPs,
    AlertSeverity

Explanation

This query is designed to monitor Microsoft 365 activities for potential data loss prevention (DLP) issues. Here's a simplified breakdown:

  1. Purpose: The query generates alerts when there is a high volume of DLP policy matches or when matches involve highly sensitive data types, such as credit card numbers or social security numbers.

  2. Scope: It examines activities from the past hour related to Microsoft 365 services like Exchange and SharePoint.

  3. Key Checks:

    • It looks for specific sensitive data types that are considered high-risk.
    • It checks if actions taken on these data matches include blocking access or notifying users.
  4. Thresholds:

    • Alerts are triggered if a user has 20 or more DLP matches in an hour or if there are 3 or more matches involving high-sensitivity data.
  5. Severity Levels:

    • The alert severity is categorized as "Critical" if there are 5 or more high-sensitivity matches.
    • It is "High" if there is at least 1 high-sensitivity match or 50 or more total matches.
    • Otherwise, it is "Medium".
  6. Output: The query outputs details such as the user ID, the number of matches, the number of high-sensitivity matches, blocked actions, involved policies, unique files or sites, client IPs, and the determined alert severity.

Overall, this query helps identify and prioritize potential data exfiltration or misuse incidents based on the volume and sensitivity of data involved.

Details

David Alonso profile picture

David Alonso

Released: March 18, 2026

Tables

OfficeActivity

Keywords

MicrosoftOfficeActivityComplianceDLPPolicyUserSensitiveInformationClientIP

Operators

letdynamicagowhereorextendtostringparse_jsonhas_anysummarizecountmake_setcountifbycaseprojectnow

Actions