← Back to list

Detecting Bulk Sensitive File Activity with Microsoft Defender and Microsoft Sentinel Summary Rules

This post walks through a practical way to catch a process touching an unusually large number of labeled, sensitive files in a short window…

Predrag · 2026-06-01 17:48 · 1 claps · 16.1 min read
#detection-engineering #microsoft-sentinel #sensitive-data #dlp #cybersecurity
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity 🥊 · Combat Sports

Detecting Bulk Sensitive File Activity with Microsoft Defender and Microsoft Sentinel Summary Rules

This post walks through a practical way to catch a process touching an unusually large number of labeled, sensitive files in a short window using Microsoft Defender for Endpoint telemetry (the DeviceFileEvents table) joined with Microsoft Purview sensitivity labels. It explains a single Kusto Query Language (KQL) detection, with particular focus on the two tuning parameters bin_window and min_files, then shows how to promote that query into a Microsoft Sentinel summary rule so the heavy aggregation runs in the background and writes compact results to a custom table. From that summary table you can build cheaper, faster detections: static thresholds, rare or first-seen process baselining, per-user and per-device statistical anomaly detection, off-hours bursts, command line inspection for archiving and copy tools, and cross-device fan-out. The final section maps the control to the Qatar 2022 Cybersecurity Framework, specifically the Security Monitoring and Data Protection capabilities, and explains how a summary rule of this kind supports the framework's requirement to collect meaningful events and detect abnormal activity against sensitive data.

Introduction: the problem with high-volume file telemetry

Most data theft, ransomware staging, and insider misuse share a common physical signature on the endpoint: a single process touches many files in a short period of time. A copy operation to a staging folder, a compression utility packaging documents before exfiltration, a backup agent gone rogue, or ransomware enumerating and rewriting files all produce a burst of file write activity. When those files carry a Microsoft Purview sensitivity label, the burst is no longer just noise, it is a signal that classified or regulated material is moving.

The difficulty is scale. Endpoint file telemetry is one of the highest-volume sources in any security data lake. A medium-sized estate can generate hundreds of millions of DeviceFileEvents rows per day. Running an interactive hunting query that aggregates distinct file paths per process across that volume is slow and expensive, and running it as a near-real-time analytics rule every few minutes multiplies the cost. You end up choosing between coverage and budget.

Microsoft Sentinel summary rules resolve that tension. They let you run a heavy aggregation on a schedule in the background, then store only the small, pre-computed result in a custom table. Detections and anomaly logic then run against that compact table instead of the raw firehose. This post builds one such control end to end, starting from the detection query and ending with framework alignment.

The telemetry source: DeviceFileEvents and sensitivity labels

The query draws from DeviceFileEvents, the Defender for Endpoint table that records file system operations observed on onboarded devices. Two points about this table are worth stating plainly because they shape the detection.

First, the action types that this table captures reliably are write-type operations: FileCreated, FileModified, FileRenamed, and FileDeleted. The table does not provide a dependable record of every file read. This matters for how you describe the control. A query filtering on FileCreated, FileModified, and FileRenamed is detecting a process that is writing, copying, or rewriting many sensitive files, not literally reading them. That is usually the more useful signal anyway, because exfiltration staging creates files at a destination, ransomware rewrites files in place, and bulk copy operations create new files. If your goal is to observe pure read access, you need a different control surface such as file system audit logs (SecurityEvent 4663) or a data security posture tool, and you should not rely on DeviceFileEvents alone.

Second, sensitivity context comes from two columns populated when Microsoft Purview Information Protection is in use:

  • SensitivityLabel, the label applied to the file to classify it for information protection. In a Qatar NIA-aligned tenant this carries the classification label name such as Internal, Limited Access, or Restricted.
  • IsAzureInfoProtectionApplied, a boolean that indicates specifically that the file is encrypted by Azure Information Protection.

Filtering on either of these narrows the firehose down to operations on files that the organization has explicitly classified or encrypted, which is exactly the population a data protection control cares about. The two conditions are joined with oron purpose: a file can carry a sensitivity label without being encrypted, and the encryption flag catches protected files even where the label string is not populated, so the combination gives the widest correct coverage of sensitive content.

One practical caveat worth confirming in your own tenant: depending on how labels are published and how the connector is configured, SensitivityLabel may contain the label display name or the label GUID. This matters because the label weighting examples later in this post match on label names. Run a quick DeviceFileEvents | distinct SensitivityLabel first and adjust the string matches to whatever your environment actually emits.

The detection query

Here is the query in full.

let bin_window = 20m;
let min_files = 50;
DeviceFileEvents
| where Timestamp > ago(1h)
| where ActionType in ("FileCreated", "FileModified", "FileRenamed")
| where isnotempty(SensitivityLabel) or IsAzureInfoProtectionApplied == true
| summarize
    SensitiveFileCount = dcount(strcat(FolderPath, "\\", FileName)),
    SampleFiles = make_set(strcat(FolderPath, "\\", FileName), 20),
    Labels = make_set(SensitivityLabel, 20),
    StartTime = min(Timestamp),
    EndTime = max(Timestamp)
    by bin(Timestamp, bin_window), DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine
| where SensitiveFileCount >= min_files
| project StartTime, EndTime, DeviceName, DeviceId, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessCommandLine, SensitiveFileCount, Labels, SampleFiles
| order by SensitiveFileCount desc

Reading it from top to bottom:

The two let statements declare the tuning parameters, covered in detail in the next section.

The first three where clauses scope the data. Timestamp > ago(1h) limits the interactive run to the last hour. The action type filter keeps only write-type operations. The sensitivity filter keeps only labeled or protected files.

The summarize block is the heart of the control. It groups events into buckets and, for each bucket, computes a set of measures. dcount(strcat(FolderPath, "\\", FileName)) counts the number of distinct full file paths the process touched, so re-touching the same file many times counts once rather than inflating the number. make_set(..., 20) captures up to twenty sample file paths and up to twenty distinct labels for the analyst, which turns a raw alert into something you can triage without pivoting back to the raw table. min(Timestamp) and max(Timestamp) record the real start and end of the activity inside the bucket.

The grouping key is deliberately rich. It bins by time and then by device identity and by the full initiating process identity, including the process file name, folder path, process id, creation time, the account domain and name, and the command line. Grouping on InitiatingProcessId together with InitiatingProcessCreationTime is what makes the unit of detection a single process instance rather than every instance of, say, explorer.exe lumped together. Including the command line means the result already carries the evidence you need to judge intent.

The penultimate where applies the volume threshold, keeping only buckets where the distinct sensitive file count reaches min_files.

project selects the output columns and order by sorts the busiest offenders to the top for interactive review.

Understanding bin_window and min_files

These two parameters are where the control is tuned, and they interact, so they deserve a dedicated explanation.

bin_window

bin_window controls the size of the time bucket that the bin(Timestamp, bin_window) function creates inside the summarize. With bin_window = 20m, all qualifying events are rounded down to the start of a fixed twenty-minute slot, and the distinct file count is computed per process per slot.

The reason a window exists at all is that volume only means something relative to time. A process that opens fifty sensitive files over a full eight-hour workday is plausibly a normal user or a sanctioned indexing service. The same fifty files inside twenty minutes is a burst, and bursts are what mass copy, compression, and encryption look like. The window converts “how many files” into “how many files how fast,” which is the dimension that separates malicious or anomalous behavior from routine work.

Choosing the window size is a balance:

  • A shorter window (for example 5m) is more sensitive to fast, aggressive bursts and produces a higher rate of fire when a process moves quickly, but it can miss a slow-and-low actor who deliberately paces operations to stay under the per-window count.
  • A longer window (for example 60m) catches paced activity and smooths over short legitimate spikes, but it raises the chance of blending several unrelated legitimate operations into one bucket and crossing the threshold by accumulation rather than by a genuine burst.

Twenty minutes is a reasonable default for endpoint copy and staging behavior. The important caveat is the relationship to the run cadence, discussed below: the bin_window should evenly divide the period the rule processes, otherwise activity that straddles a bucket boundary gets split across two buckets and each half may fall under the threshold. A run that processes one hour of data with a twenty-minute bin yields up to three clean buckets per process.

min_files

min_files is the volume threshold. After bucketing, the query keeps only buckets where the distinct count of sensitive files reaches this value, set to 50 here.

This parameter is the single biggest lever on alert volume and false positive rate. Set it too low and routine activity such as a user saving a folder of labeled documents, a sanctioned sync client, or a legitimate batch process will trip it constantly. Set it too high and a careful adversary who exfiltrates in modest batches will never cross the line.

The right value is environment specific and should be derived from your own data rather than copied. A practical approach is to run the aggregation without the final threshold over a representative training period, look at the distribution of SensitiveFileCount per process per window, and place min_files above the bulk of normal behavior. Many teams set it near a high percentile of the observed distribution, for example the 99th percentile, so that the static rule fires only on clear outliers and the subtler cases are left to the statistical anomaly logic described later.

A useful mental model: bin_window defines the unit of time, min_files defines the volume that is suspicious within that unit, and together they express a rate. Changing one almost always means revisiting the other. Doubling the window without raising the threshold makes the rule more permissive about what counts as a burst.

Promoting the query to a Microsoft Sentinel summary rule

A summary rule in Microsoft Sentinel is a scheduled KQL query that aggregates high-volume data in the background and writes the result to a custom log table in the analytics tier. The aggregated output is small and pre-computed, so later queries against it run quickly and cheaply, including over data that originally lived in lower-cost log tiers. Summary rules can run on a frequency between twenty minutes and twenty-four hours, and they are created from the Configuration area of Microsoft Sentinel, where you give the rule a name, a description, a destination table, and the query body.

This detection is a strong candidate for a summary rule for three reasons. The aggregation is expensive because it computes distinct counts across a very high-volume table. The output is tiny because only a handful of processes per hour will breach a sensibly set threshold. And the result is exactly the shape you want to keep for a long time for reporting and historical investigation, while the raw DeviceFileEvents data can be retained for a shorter, cheaper period.

Adapting the query for a summary rule

Two changes are needed when moving from an interactive hunt to a summary rule.

First, remove the | where Timestamp > ago(1h) line. The summary rule defines its own bin period and look-back when you schedule it, and that schedule governs the time window the query sees on each run. A hardcoded ago(1h) would conflict with the rule's configured period and produce inconsistent or empty results. Let the rule's schedule own the time scope, and keep only the internal bin(Timestamp, bin_window) to create sub-buckets within each run.

Second, drop the trailing | order by SensitiveFileCount desc. Sorting is meaningful for interactive display but has no effect on rows written to a table, and it adds cost for nothing.

The resulting summary rule query:

let bin_window = 20m;
let min_files = 50;
DeviceFileEvents
| where ActionType in ("FileCreated", "FileModified", "FileRenamed")
| where isnotempty(SensitivityLabel) or IsAzureInfoProtectionApplied == true
| summarize
    SensitiveFileCount = dcount(strcat(FolderPath, "\\", FileName)),
    SampleFiles = make_set(strcat(FolderPath, "\\", FileName), 20),
    Labels = make_set(SensitivityLabel, 20),
    StartTime = min(Timestamp),
    EndTime = max(Timestamp)
    by bin(Timestamp, bin_window), DeviceId, DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessCreationTime, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine
| where SensitiveFileCount >= min_files
| project StartTime, EndTime, DeviceName, DeviceId, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessCommandLine, SensitiveFileCount, Labels, SampleFiles

Schedule this to run hourly with a one-hour bin period, set the destination to a new custom table (Sentinel appends the _CLsuffix automatically, giving a name such as SensitiveFileBurst_CL), and enable the SummaryLogs diagnostic setting on the workspace so you have visibility into rule runs and failures. With a one-hour run period and a twenty-minute internal bin, each execution produces up to three buckets per process, and only those that reach min_files are stored.

A practical note on the resulting table schema: custom log tables created this way typically add type suffixes to column names, so InitiatingProcessAccountName may appear as InitiatingProcessAccountName_s and SensitiveFileCount as a numeric column with its own suffix. Check the actual schema of your table after the first run and adjust the column names in the downstream queries below to match what you see.

Detection use cases on the summary table

Because the summary table already contains only the bursts that crossed the threshold, detections built on it are cheap and fast. The patterns below are generic and apply to the summary table regardless of how you tuned the original parameters.

Static threshold and label weighting

The simplest analytics rule alerts whenever a new row lands in the summary table, since by construction every row already represents a process that exceeded min_files. You can raise the priority of rows whose Labels set contains your most restrictive classifications. Under the Qatar NIA scheme used by the framework (described in the next section), a burst of Restricted (C3) or nationally classified (C4) files is higher severity than a burst of merely Internal (C1) ones.

SensitiveFileBurst_CL
| where TimeGenerated > ago(1h)
| extend HighImpact = Labels has_any ("Restricted", "Limited Access", "Confidential", "Secret", "Top Secret")
| extend Severity = iff(HighImpact, "High", "Medium")

Suspicious process location and known archiving or copy tools

Bursts originating from processes running out of user-writable or transient directories, or from known compression and bulk-copy utilities, deserve closer scrutiny. The summary table carries both the process folder path and the command line, so this requires no return trip to the raw data.

SensitiveFileBurst_CL
| where InitiatingProcessFolderPath has_any (@"\appdata\", @"\temp\", @"\users\public\", @"\programdata\")
    or InitiatingProcessFileName in~ ("7z.exe", "winrar.exe", "rar.exe", "tar.exe", "robocopy.exe", "xcopy.exe")
    or InitiatingProcessCommandLine has_any ("compress", "-hp", "Compress-Archive")

Cross-device fan-out by a single account

A single account producing sensitive file bursts across many distinct devices in a short period is a strong indicator of credential compromise or lateral movement, a pattern that is invisible at the level of any single device.

SensitiveFileBurst_CL
| where TimeGenerated > ago(6h)
| summarize DeviceCount = dcount(DeviceId), TotalFiles = sum(SensitiveFileCount), Devices = make_set(DeviceName, 25)
    by InitiatingProcessAccountName
| where DeviceCount >= 3

Off-hours activity

Sensitive file bursts outside the user’s normal working hours are worth flagging on their own, since legitimate bulk operations usually track business activity.

SensitiveFileBurst_CL
| extend Hour = datetime_part("hour", TimeGenerated)
| where Hour < 6 or Hour >= 20

Anomaly detection use cases on the summary table

Static thresholds catch the obvious. Anomaly detection catches behavior that is unusual relative to a baseline even when it stays below a fixed number. Building a longer-term baseline directly on raw DeviceFileEvents would be costly, but the summary table is small enough to baseline freely. To make anomaly detection meaningful, it can help to lower min_files in the summary rule slightly so the table captures a broader spread of activity to learn from, then apply the statistical logic below to find the outliers.

Per-entity statistical baseline with a z-score

Compute a mean and standard deviation of burst size for each account and device over a trailing baseline period, then flag current bursts that sit far above their own entity’s normal level. This catches the user who normally peaks at twenty files but suddenly hits one hundred and twenty, even if your static threshold sat at two hundred.

let baseline_window = 30d;
let current_window = 1d;
let sensitivity = 3.0;
let baseline =
    SensitiveFileBurst_CL
    | where TimeGenerated between (ago(baseline_window) .. ago(current_window))
    | summarize avgCount = avg(SensitiveFileCount), stdevCount = stdev(SensitiveFileCount)
        by InitiatingProcessAccountName, DeviceName;
SensitiveFileBurst_CL
| where TimeGenerated > ago(current_window)
| join kind=inner baseline on InitiatingProcessAccountName, DeviceName
| extend zscore = iff(stdevCount > 0, (SensitiveFileCount - avgCount) / stdevCount, 0.0)
| where zscore > sensitivity
| project TimeGenerated, InitiatingProcessAccountName, DeviceName, SensitiveFileCount, avgCount, stdevCount, zscore

The guard on stdevCount > 0 prevents division by zero for entities with no historical variation.

Time series decomposition

For account or device level trends, the built-in series_decompose_anomalies function models seasonality and trend and scores points that deviate from the expected pattern. This is well suited to spotting a sustained shift in behavior rather than a single spike.

let lookback = 14d;
let binsize = 1h;
SensitiveFileBurst_CL
| where TimeGenerated > ago(lookback)
| make-series Total = sum(SensitiveFileCount) default = 0
    on TimeGenerated step binsize
    by InitiatingProcessAccountName
| extend (anomalies, score, baseline) = series_decompose_anomalies(Total, 2.5, -1, 'linefit')
| mv-expand TimeGenerated to typeof(datetime), Total to typeof(long), anomalies to typeof(double), score to typeof(double)
| where anomalies == 1

First-seen and rare process or account-device pairs

A process or an account-device pairing that produces a sensitive file burst for the first time after a stable training period is inherently interesting. A left anti-join against the historical set surfaces these new combinations without any threshold tuning.

let training = 21d;
let detection = 1d;
let known =
    SensitiveFileBurst_CL
    | where TimeGenerated between (ago(training) .. ago(detection))
    | distinct InitiatingProcessFileName, DeviceName;
SensitiveFileBurst_CL
| where TimeGenerated > ago(detection)
| join kind=leftanti known on InitiatingProcessFileName, DeviceName

The same pattern applied to InitiatingProcessAccountName and DeviceName reveals accounts handling sensitive files on devices they have never used that way before.

Aligning the control with the Qatar 2022 Cybersecurity Framework

The Qatar 2022 Cybersecurity Framework was issued for entities and partners supporting the FIFA World Cup 2022 and has continued to serve as a baseline for organizations working with Qatari government bodies. It is built as a set of capability chapters grouped under pillars including Prevention and Detection, and its activities are cross-referenced throughout to established baselines including the Qatar National Information Assurance (NIA 2.0) standard, ISO/IEC 27001, NIST SP 800–53, HIPAA, the Cloud Security Alliance Cloud Controls Matrix, and IEC 62443. Because of that cross-referencing, a single technical control mapped to the framework typically satisfies several of those standards at once. Three parts of the framework are directly relevant to a control that detects bulk activity against classified files: the data classification scheme, the Data Protection capability (Chapter 6, Prevention pillar), and the Security Monitoring and Operations capability (Chapter 8, Detection pillar).

Data classification: the C1 to C4 scheme

Section 6.9 of the framework defines four classification categories above Public, drawn from NIA 2.0. These are the labels a Microsoft Purview deployment should be configured to apply, and they are what should appear in the SensitivityLabelcolumn the detection filters on.

This is why the label weighting shown earlier elevates Restricted, Limited Access, and the C4 markings: a burst against C3 or C4 material is a materially more serious event than a burst against C1, and the control can express that distinction directly because the classification travels with the file telemetry.

Data Protection capability (Chapter 6, Prevention pillar)

The Data Protection chapter defines this capability as one that prevents classified information from leaving an entity’s boundaries without authorization. Its Data in Use guidance is strikingly close to what this detection observes. The framework calls for technologies that react to user actions such as copying classified data and files to removable media or out of a data repository in violation of policy, and it states the objective as understanding data loss risks across the enterprise by analysing suspicious events generated by the endpoints. Within the same capability, the Access and usage monitoring service calls for monitoring access and usage of high-risk data to identify potentially inappropriate usage using endpoint DLP and SIEM logs, the Privileged user monitoring service calls for watching users who can perform mass data extracts, and the Export and save control service addresses restricting bulk copying of sensitive data. A summary rule that flags a process touching dozens of classified files in minutes is a concrete, endpoint-sourced implementation of exactly that intent.

Security Monitoring and Operations capability (Chapter 8, Detection pillar)

The Security Monitoring service in Chapter 8 lays out a Collection, Fusion, Analysis, and Action lifecycle. The detection maps onto it cleanly. DeviceFileEvents is the determined and reviewed audit log record the Collection phase requires. The summary rule performs the Fusion phase by aggregating and correlating event data into a compact form. The threshold and anomaly logic carry out the Analysis phase, which the framework describes as establishing incident alert thresholds and monitoring personnel activity to detect potential cybersecurity events. Alerting into the SOC is the Action phase. The chapter's Threat Hunting service reinforces this further, since it explicitly describes evaluating information collected by security monitoring tools for anomalous activities and automating the hunts by defining a use case in the SIEM, which is precisely the lifecycle of taking this hunting query and operationalising it as a summary rule with detections layered on top.

The framework also publishes a list of required SIEM use cases in section 8.10.2.2, and this single control contributes to several of them at once. It directly supports use case 46, detection of ransomware, because mass rewriting of classified files is the on-disk signature of encryption. It supports use case 47, detection of PowerShell-based attacks, when the captured InitiatingProcessCommandLine shows staging or compression invoked through PowerShell. It contributes evidence to use case 4, remote access to sensitive data, and use case 41, complete user activity tracking for selected users, since the summary table records the account, device, process, and command line behind every burst.

A note on precision for attestations

The capability and use-case alignment above is taken from the framework document itself, so the mapping to Chapter 6, Chapter 8, the C1 to C4 scheme in section 6.9, and the required use cases in section 8.10.2.2 is sound. For a formal compliance attestation, confirm the exact activity rows and the cross-standard control identifiers (the framework’s per-activity mapping tables reference NIA 2.0, ISO 27001, NIST SP 800–53, HIPAA, CSA CCM, and IEC 62443) against your licensed copy, since those identifiers are the level auditors will expect to see cited.

Conclusion

A burst of write activity against labeled, sensitive files is one of the clearest endpoint signatures of data theft, ransomware staging, and insider misuse, and DeviceFileEvents combined with Purview sensitivity labels gives you the raw signal to detect it. The two parameters at the center of the control, bin_window and min_files, together express a rate rather than a count, and tuning them against your own data is what separates a noisy rule from a useful one. Promoting the aggregation to a Microsoft Sentinel summary rule moves the expensive distinct-count work into the background, leaves you with a small and durable table, and unlocks a layer of cheap detections and statistical anomaly logic that would be impractical to run against the raw firehose. Mapped to the Qatar 2022 Cybersecurity Framework, the same control speaks to the Security Monitoring and Data Protection capabilities at once, turning a single well-built query into both an operational detection and a piece of demonstrable compliance evidence.

References


메타데이터
post_id
3a64ecd21a1a
slug
detecting-bulk-sensitive-file-activity-with-microsoft-defender-and-microsoft-sentinel-summary-rules-3a64ecd21a1a
url
https://medium.com/@0xrick/detecting-bulk-sensitive-file-activity-with-microsoft-defender-and-microsoft-sentinel-summary-rules-3a64ecd21a1a
canonical_url
https://medium.com/@0xrick/detecting-bulk-sensitive-file-activity-with-microsoft-defender-and-microsoft-sentinel-summary-rules-3a64ecd21a1a
author_url
https://medium.com/@0xrick
status
ok
fetched_at
2026-06-10 09:45:21