The settings change arms it. The encryption executes it. Raising the limit back returns nothing.
Ransomware in a Microsoft 365 tenant does not look like ransomware on a network. There is no encrypted server to rebuild, no ransom note on a desktop, and frequently no alert at all. What there is instead is a document library that will not open and a user who mentions it the following morning.
The response everybody reaches for is version history. It is the right instinct: SharePoint and OneDrive keep prior versions of every file, the default is 500 of them, and rolling a library back to yesterday morning is an afternoon's work. That instinct is why the technique below exists.
The setting that decides whether you recover
Version history limits are configurable at organization, site and library level. The organization default applies to new libraries. Site owners can override it for sites they own, and they can override it again for individual libraries and lists.
Read that last sentence in security terms. The number of versions a library keeps is set by whoever owns the library, not by an administrator. No elevated role is required, the setting lives under ordinary list settings, and nothing about it is presented as a security control.
So an attacker holding an ordinary compromised account with owner rights over a site can reduce the version limit on a library, and everything that follows is arithmetic.
When a count limit is lowered, existing versions above the new limit are not deleted immediately. They are trimmed as each file is next updated. An encryption pass updates every file it touches. Reduce the limit to one, modify each file twice, and the clean versions are removed by the attacker's own edits rather than by any deletion operation.
The published technique has two forms. The slow one is 501 edits per file, pushing the original off the end of a 500-version chain. It works, and it runs all night and produces a volume spike anybody watching would see. The fast one is a settings change and two edits, which is minutes of work through the API and produces roughly an ordinary busy evening of file activity.
Numbers worth checking against your own tenant. The organization default is 500 major versions. The portal will not accept a value below 100, and the API is not bound by that floor. Organization-level settings do not apply to existing sites and libraries, which keep whatever they already had, so a tenant-wide change made last year describes new content and nothing else. And the recycle bin is irrelevant here: nothing is deleted, so it stays empty throughout.
Why raising the limit back does not help
This is the part that costs organizations a day. The instinct on discovering a limit of 1 is to set it back to 500 and restore.
Trimmed versions are gone rather than hidden. The version chain is a property of the file, and the entries removed during the encryption pass no longer exist to be restored. Setting the limit higher changes what will be kept from now on, and returns nothing that was already trimmed.
What remains available is a point-in-time rollback of the whole library, which undoes every action across the chosen window, including legitimate work. That is a recovery and it is a blunter one than versions would have been.
Why nothing alerted
Worth pausing on the shape of this before the detection, because it explains why an estate with good coverage sees none of it.
Every operation in the sequence is performed by an authorized account doing something the product permits. Changing a version limit is a site owner exercising a right the platform grants them. Modifying a file is the most ordinary thing anybody does in SharePoint all day. There is no privilege escalation to spot, no unusual admin role assignment, no malware on an endpoint, and no failed authentication anywhere in the chain.
Identity-focused detection sees a session that authenticated successfully. Endpoint detection sees nothing at all, because none of this touched an endpoint the tenant controls. Data loss prevention sees modifications rather than exfiltration. The one control that would have fired is a rule on a configuration change nobody thought to write, because the setting is filed under storage management rather than under security.
That is the general lesson worth taking even if you never meet this specific technique. The operations that remove your ability to recover are frequently not the operations that look like an attack, and they are frequently configuration rather than access.
Detecting the settings change
Microsoft logs audit events for changes to version history limits at organization, site and library level. That is the detection surface, and it has unusually good properties: the operation is rare in a healthy estate, it carries an actor and a timestamp, and a single occurrence is worth a human looking at it.
Most detection engineering fights a baseline. This one barely has one.
// Hypothesis: an actor reduces a library's version limit to remove the
// recovery path before encrypting. Rare operation, near-empty baseline,
// so alert on occurrence rather than on volume.
//
// CONFIRM THE OPERATION STRING against your own tenant before deploying:
// run the query with the where clause removed over 30 days, read the
// distinct Operation values for version-limit changes, and pin to what
// your tenant actually emits. Do not take a string from a blog post.
OfficeActivity
| where TimeGenerated > ago(30d)
| where RecordType has "SharePoint"
| where Operation has_any ("VersionLimit", "Versioning", "ListUpdated")
| where tostring(Parameters) has_any ("MajorVersionLimit", "VersionLimit")
| project TimeGenerated, UserId, Operation, OfficeObjectId, ClientIP, Parameters
| sort by TimeGenerated descThe pairing is what turns it into a finding rather than an alert. A settings change on its own may be somebody managing storage. A settings change followed within the hour by a burst of modifications from the same account is the technique.
// The pairing. A limit change, then heavy modification of the same site
// by the same account inside 60 minutes.
let changes =
OfficeActivity
| where TimeGenerated > ago(7d)
| where tostring(Parameters) has "MajorVersionLimit"
| project ChangeTime = TimeGenerated, UserId, Site = OfficeObjectId, ClientIP;
let bursts =
OfficeActivity
| where TimeGenerated > ago(7d)
| where Operation == "FileModified"
| summarize Mods = count(), Files = dcount(OfficeObjectId),
FirstMod = min(TimeGenerated)
by UserId, bin(TimeGenerated, 1h)
| where Mods > 100;
changes
| join kind=inner bursts on UserId
| where FirstMod between (ChangeTime .. (ChangeTime + 60m))
| project ChangeTime, FirstMod, UserId, Site, ClientIP, Mods, Files,
ModsPerFile = round(todouble(Mods) / todouble(Files), 1)That last column is the tell. Two modifications per file across hundreds of files is not how people work. It is what the fast form of this technique produces.
The same logic in Splunk, against the Office 365 management activity input:
* Version limit change followed by a modification burst, same user, 60 minutes
index=o365 sourcetype="o365:management:activity" Workload=SharePoint
| search Parameters="*MajorVersionLimit*"
| eval change_time=_time
| table change_time UserId ObjectId ClientIP
| join type=inner UserId
[ search index=o365 sourcetype="o365:management:activity" Operation=FileModified
| bin _time span=1h
| stats count AS mods dc(ObjectId) AS files min(_time) AS first_mod BY UserId _time
| where mods > 100 ]
| eval window_end=change_time+3600
| where first_mod >= change_time AND window_end >= first_mod
| eval mods_per_file=round(mods/files, 1)
| table change_time first_mod UserId ObjectId ClientIP mods files mods_per_fileAnd as Sigma, so it travels:
title: SharePoint Version History Limit Reduced
id: 8c1f4a92-6d3e-4b17-9a2c-f5e08b31d47a
status: experimental
description: >
Detects a change to version history limits on a SharePoint or OneDrive
library. Rare in normal operation and a documented precursor to cloud
ransomware, because reducing the limit trims clean versions as each file
is next updated.
references:
- https://learn.microsoft.com/en-us/sharepoint/document-library-version-history-limits
- https://attack.mitre.org/techniques/T1490/
author: Ridgeline Cyber
date: 2026/09/01
tags:
- attack.impact
- attack.t1490
- attack.t1486
logsource:
product: m365
service: sharepoint
detection:
selection:
Workload: SharePoint
Parameters|contains: 'MajorVersionLimit'
condition: selection
falsepositives:
- Storage remediation by a site owner or administrator. Every version is a
full copy of the file, so reducing limits is a legitimate and common way
to reclaim space. Correlate with modification volume from the same
account before escalating.
level: mediumWhere to run it
Both queries above assume the Office 365 management activity is reaching a workspace you can query with a lookback of more than a few days. If your organization reads SharePoint activity only through the Purview audit search, the single-event rule still works as a scheduled search and the pairing is harder, because correlating two operation types across an hour is what a workspace is for.
That distinction is worth establishing before you write either rule. A detection you cannot schedule is a hunt, and a hunt nobody has scheduled is a good intention.
The false positive is real and it is the point
Reducing a version limit is a normal thing to do. Every version is a full copy of the file rather than a delta, so a 200 MB presentation with ten versions occupies two gigabytes. Across a large library, version history becomes a substantial share of a tenant's consumption, and somebody reducing a limit is usually solving a genuine cost problem.
That is exactly what makes the technique work. The malicious change and the legitimate change are indistinguishable in isolation. What separates them is what happens next, which is why the pairing query matters more than the single-event rule.
Set the single-event rule to medium and route it for review. Set the paired rule to high and wake somebody.
What the pairing cannot see
One honest limitation. The pairing rule keys on modification volume, and the fast form of this technique produces two modifications per file. Across four hundred files that is eight hundred operations, which clears a hundred-operation threshold easily. Across twenty files it is forty, and it does not.
A patient actor working a small, high-value library stays under any volume threshold you set. What still catches them is the single-event rule on the settings change, which is why it is worth deploying even though it will surface legitimate storage work. The volume rule catches the loud version; the settings rule catches both and costs you a review queue.
What this means for your recovery position
Three things follow that are worth establishing before you need them.
Your recovery ceiling is whatever a compromised site owner leaves it. If your organization holds no independent backup of SharePoint content, then version history and the recycle bin are the whole of your file recovery, and one of the two is configurable by an ordinary user. Microsoft's own Services Agreement recommends that customers back up their own data; infrastructure redundancy keeps the service available and faithfully replicates whatever state exists, including an encrypted one.
The recycle bin will not help and its emptiness is not reassuring. Nothing is deleted in this technique. An empty bin during a suspected ransomware event tells you the problem is modification rather than deletion, which is a useful diagnostic and not a comfort.
Check the setting rather than trusting it. The most valuable ninety seconds in the incident this post is drawn from was somebody reading the current version limit on the affected library rather than assuming it was the configured 500. Everything after that decision followed from what it found.
What to do this week
- Read the current version limit on three libraries whose loss would matter. Not the tenant default and not what you believe it to be. Site settings, then library settings, then the actual number.
- Establish who owns those sites, by name. Every one of them can change that limit without holding an administrative role, and per the section above, so can anybody holding their session.
- Run the first query over 30 days with the
whereclause relaxed, read the distinct operation values your tenant actually emits for version-limit changes, and pin the rule to those rather than to the strings in this post. - Deploy the paired rule at high and the single-event rule at medium. The single event is a review item; the pair is an incident.
- Ask whoever owns the platform whether independent backups of SharePoint content exist, and write the answer down with a date and a name. It takes one message and it bounds every recovery decision you will ever make in that tenant.
The technique is not clever and it does not need to be. It relies on a setting nobody watches, in a place nobody thinks of as a security control, changed by an account that does not need to be privileged. The detection is cheap, the baseline is nearly empty, and almost nobody has written the rule.