On May 18, 2026, the Microsoft Threat Intelligence team published a detailed write-up of a financially motivated cluster they track as Storm-2949. The actor turned a single phone call into a full Microsoft 365 and Azure compromise. No malware, no zero-day, no novel exploit. The whole intrusion ran on features that ship enabled by default in every Microsoft 365 tenant: Self-Service Password Reset, Microsoft Authenticator enrollment, the Microsoft Graph API, and a handful of Azure RBAC roles assigned to a single overworked admin.
The chain looked like this. Storm-2949 called the help desk impersonating a target user, walked the agent through the Self-Service Password Reset flow, and got an MFA prompt approved through social pressure. Microsoft's reporting confirms the actor then reset the password, deleted the legitimate authentication methods, and re-enrolled Microsoft Authenticator on an attacker-controlled device. From there they ran a custom Python script against Microsoft Graph to enumerate users, role assignments, applications, and service principals; pivoted into OneDrive and SharePoint and downloaded thousands of files in a single Graph batch; then used Azure Key Vault permissions tied to the compromised identity to lift "dozens" of secrets including database connection strings and SAS tokens. Persistence came from a ScreenConnect install pushed to an Azure VM via the VMAccess and Run Command extensions.
None of this required a CVE. The defensive read for a small or mid-size business is that the same default-on features sit in your tenant right now. SSPR is enabled in every Microsoft 365 Business Premium, E3, and E5 tenant on day one. Authenticator enrollment is permitted from any device. The "Application Administrator" and "Privileged Role Administrator" roles are routinely held by people who do not realize they own a path to every Graph permission in the directory. This article is the SMB read of Storm-2949: how to close the front door this week, what to hunt in your audit logs tomorrow, and where to spend the consulting hour you are willing to invest.
What Storm-2949 actually did, in technical terms
The Microsoft write-up names the actor's tradecraft step by step, and the chain is worth reading literally so the defensive choices map cleanly. The whole sequence ran in days, not weeks.
Initial access: social engineering plus SSPR. Storm-2949 targeted IT personnel and senior leadership, the two groups most likely to be granted privileged Entra ID roles. The attacker initiated the SSPR flow against the target's account from https://passwordreset.microsoftonline.com, then called the target posing as an IT support agent. The verification step the user "approved" was the MFA prompt Microsoft sent to authorize the password reset. Once the prompt was accepted, the actor set a new password, removed the user's existing authentication methods, and registered their own Microsoft Authenticator instance. The legitimate user was now locked out of their own account, with the attacker holding the only working MFA factor.
Recon: Microsoft Graph batch enumeration. With a working session the actor ran a Python script against Graph endpoints to enumerate users, directory roles, service principals, and group memberships. The script filtered by display name patterns and role attributes to identify high-value accounts, exactly the way a red team would. This is one of the cheapest detections you have: bulk Graph reads from a user account inside the first hour of sign-in are not normal behavior for a finance manager or a sales VP.
Data theft: OneDrive, SharePoint, and Key Vault. Storm-2949 pulled IT documentation, VPN configurations, and operational runbooks out of OneDrive and SharePoint in a single bulk Graph download. They then pivoted to Azure, used the compromised identity's RBAC assignments to enumerate Key Vault resources, and exfiltrated database credentials and connection strings. Microsoft's write-up specifically calls out modification of Key Vault access policies and "dozens of secrets" pulled inside a roughly four-minute window. That is a control-plane signal a properly tuned Key Vault diagnostic setting will catch on the first event.
Lateral movement: Azure VM extensions. The actor used microsoft.Compute/virtualMachines/runCommand/action and the VMAccess extension to add a local administrator account on a target VM, then deployed ScreenConnect over the Run Command channel. ScreenConnect callouts in the Microsoft write-up land at 185.241.208[.]243, with attacker egress observed at 176.123.4[.]44 and 91.208.197[.]87. Egress filtering and a deny-by-default VM extension policy would have ended the chain here.
Cleanup. The actor attempted to disable Microsoft Defender on compromised hosts and cleared event logs before leaving. This step is the operational fingerprint. Defender tamper-protection alerts and Security event ID 1102 (audit log cleared) should already be tier-one in your SIEM. If they are not, fix that this sprint regardless of Storm-2949.
Why SSPR is the SMB problem here
Microsoft's own product documentation, the SSPR reporting how-to, treats the feature as a help desk efficiency win. For SMBs that is exactly why it gets enabled and never tuned. The default deployment looks like this: SSPR on for all users, two authentication gates (phone or email), Microsoft Authenticator allowed for any method, and admin accounts swept into the same policy as everyone else because nobody scoped a separate one. That posture is fine for self-service password resets done by the people the policy was designed for. It is also exactly what Storm-2949 ran against.
The specific gaps Storm-2949 exercised, all of which are tunable inside the Entra admin center:
- SSPR scope. "All users" is the default. Admin accounts and break-glass accounts should be excluded by group. Microsoft has supported group-scoped SSPR for years; most tenants do not use it.
- Allowed authentication methods. SMS and voice call are the path of least resistance for a help desk impersonation. Phishing-resistant methods (FIDO2 security keys, Windows Hello for Business, certificate-based authentication) are not enabled by default for SSPR.
- Authentication method registration. Adding a new MFA method is a self-service action. There is no required out-of-band approval, no waiting period, and no notification to an admin when an attacker registers Authenticator on a new device.
- Privileged role assignment. Most SMB tenants have at least three permanent Global Administrators, no Privileged Identity Management activation, and standing assignments on roles like Application Administrator and Cloud Application Administrator that can mint client secrets on any service principal in the tenant.
- Key Vault diagnostic logging. The default for new Key Vaults is no diagnostic settings. SecretGet operations are not visible until you wire
AzureDiagnosticsorAzureActivityto a Log Analytics workspace or storage account.
None of those changes require buying a product. They require an hour with the right admin account and a willingness to break a help desk workflow that has been comfortable for a long time.
The 48-hour Entra ID hardening checklist
This is the work to do this week. Do them in order. None of these break a normal user; all of them break Storm-2949.
1. Scope SSPR away from privileged roles
In the Entra admin center, navigate to Protection > Password reset > Properties. Change Self-service password reset enabled from All to Selected, then point it at a group that explicitly excludes every Entra ID role-assigned account. Privileged users get their passwords reset by another admin, not via the self-service portal. Microsoft's SSPR policy reference documents the group scoping behavior.
2. Require two strong methods and remove SMS where you can
Under Protection > Password reset > Authentication methods, set the Number of methods required to reset to 2 and uncheck Mobile phone (SMS only) as an allowed method for non-administrators where business workflow allows it. The 2026 reality is that SMS is the easiest channel to socially engineer through a US carrier, and Microsoft itself has been encouraging the move away from SMS for two years.
3. Lock down MFA registration to trusted networks or compliant devices
Under Protection > Conditional Access, create a policy named Block MFA registration from untrusted locations. Target it at user action Register security information, scope it to all users excluding break-glass, and require either a compliant device or a trusted network IP. This single policy would have prevented Storm-2949's Authenticator enrollment because the attacker's enrollment came from 176.123.4[.]44, not your VPN range.
4. Turn on Privileged Identity Management for every directory role
For Microsoft 365 Business Premium tenants without Entra ID P2, the workaround is to keep standing assignments to zero and use a manual approval process. For every other tenant, enable PIM (Identity governance > Privileged Identity Management > Microsoft Entra roles) and convert all role assignments to eligible-with-activation. Storm-2949 exploited standing assignments; eligible-with-activation forces the attacker to also clear a fresh MFA challenge inside the elevation window, which the help desk channel does not give them.
5. Enable Key Vault diagnostic logging on every vault, today
For every Key Vault in scope, set Diagnostic settings to forward AuditEvent to your Log Analytics workspace and to a write-once storage account. This is the bare minimum to detect the four-minute bulk-secret pull Storm-2949 executed. Microsoft's Key Vault logging documentation covers the operation list.
The hunt: three queries to run before next week
If you have Microsoft Defender XDR, Microsoft Sentinel, or any Log Analytics workspace ingesting the AuditLogs and SigninLogs tables, run these three KQL queries. Each one targets a step in the Storm-2949 chain. None of them require a paid threat intel feed.
// 1. SSPR resets followed by an authentication method change within 30 minutes.
// This is the exact "reset and re-enroll" pattern Storm-2949 used.
AuditLogs
| where TimeGenerated > ago(30d)
| where Category in ("UserManagement", "AuthenticationMethods")
| where OperationName in ("Reset password (self-service)",
"User registered security info",
"Admin registered security info",
"User deleted security info")
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend Target = tostring(TargetResources[0].userPrincipalName)
| project TimeGenerated, OperationName, Actor, Target, Result
| order by Target asc, TimeGenerated asc
| serialize
| extend PrevOp = prev(OperationName), PrevTime = prev(TimeGenerated), PrevTarget = prev(Target)
| where Target == PrevTarget
and PrevOp == "Reset password (self-service)"
and OperationName != "Reset password (self-service)"
and TimeGenerated - PrevTime < 30m
// 2. Bulk Microsoft Graph reads from a single user session.
// Storm-2949's enumeration script pulled hundreds of objects in minutes.
SigninLogs
| where TimeGenerated > ago(7d)
| where AppDisplayName in ("Microsoft Graph PowerShell",
"Microsoft Graph Command Line Tools",
"Microsoft Graph Explorer")
or ResourceDisplayName == "Microsoft Graph"
| summarize Sessions = dcount(CorrelationId),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
IPs = make_set(IPAddress, 5)
by UserPrincipalName, AppDisplayName
| where Sessions > 1
| order by Sessions desc
// 3. Key Vault secret reads at rate, by single principal.
// The four-minute "dozens of secrets" pull is a clear control-plane signal.
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName in ("SecretGet", "SecretList", "KeyGet")
| summarize Pulls = count(),
VaultsTouched = dcount(Resource),
FirstPull = min(TimeGenerated),
LastPull = max(TimeGenerated)
by identity_claim_upn_s
| extend WindowMinutes = datetime_diff('minute', LastPull, FirstPull)
| where Pulls > 10 and WindowMinutes < 15
Save these as named hunts in your workspace. The first one is the highest signal-to-noise pattern in the set; in a healthy tenant it should return zero rows over thirty days. Anything it does return is worth a phone call to the user, the way Storm-2949 wishes you had.
What this looks like for an SMB without an in-house SOC
If you are a thirty-person firm with no security engineer on staff, the answer is not "stand up Sentinel by Friday." The realistic answer is sequenced. Today, do steps 1, 2, and 3 of the hardening checklist; they need one admin and an hour. Tomorrow, get your MSP or fractional security partner to scope your privileged role assignments and turn on PIM where licensing allows. This week, point a Log Analytics workspace at AuditLogs, SigninLogs, and Key Vault AuditEvent, and schedule the three queries above to run nightly with an email alert on any rows returned.
Storm-2949 is not exotic. The same playbook will run against your tenant by August under a different cluster name. The thing that gets organizations breached this way is not a missing product; it is a help desk culture that treats the SSPR portal as a productivity feature instead of an authentication path. Move it back this week.
Need help hardening your identity infrastructure?
We assess Active Directory and Entra ID environments for the misconfigurations attackers actually exploit, including SSPR scoping, privileged role audits, and Key Vault telemetry. Book a session to walk your tenant.
