Multi-factor authentication was switched on, and the attacker logged in anyway. On June 22, the worst day of a two-week password spray against Microsoft Azure, 30 accounts fell across 23 organizations, and 15 of those organizations had MFA enforced through a Conditional Access Policy. The second factor never fired. It never fired because the login never passed through the door where the policy waits. That gap, not the 81 million login attempts behind it, is the reason this campaign should hold your attention if you run anything in Entra ID.
Huntress tracked the activity from June 12 to June 26 and counted more than 81 million login attempts that compromised 78 accounts across 64 organizations. The traffic came almost entirely from an IPv6 range operated by an infrastructure provider called LSHIY, whose networks trace back to Hong Kong and Wuhan. The targeting looks opportunistic: whichever usernames and passwords showed up most often in old breach lists got tried first. None of that is the interesting part. The interesting part is the sign-in method the attacker chose.
The number to remember is 15, not 81 million
Volume makes a good headline, and 81 million attempts is a big one. But a password spray only pays off when the password is right and nothing stops the login after that. The 15 organizations that had MFA and were breached anyway are the finding that should change what you do on Monday. They did the thing the security industry has told everyone to do for a decade, and it was not enough, because their MFA guarded the front door while the attacker used a side entrance.
How a retired login protocol walks past Conditional Access
Azure CLI, the command-line tool for managing Microsoft's cloud, still accepts a legacy sign-in method called Resource Owner Password Credentials, or ROPC. In a normal browser sign-in you are redirected to Microsoft's authorization endpoint, the interactive page that can stop and challenge you: prompt for a second factor, check whether your device is managed, look at where you are connecting from. Conditional Access lives on that page. ROPC skips it. The client takes a username and password directly and trades them at the token endpoint for an access token, with no interactive step in between. There is no page on which to raise an MFA prompt, so for accounts reached this way, none appears. Security researchers have described the same gap: legacy grants like ROPC never touch the endpoint where the policy is enforced.
ROPC was deprecated in the OAuth 2.1 draft for exactly this reason, and Microsoft discourages it, yet Azure CLI keeps it available for backward compatibility. That leaves a first-party Microsoft application, one that most tenants trust without a second thought, quietly accepting username-and-password logins that route around the policy engine. An administrator who ticks the box for MFA and sees it enforced in the portal can reasonably believe the tenant is covered. The sign-in records from this campaign say otherwise: valid password, single factor, success.
Old passwords never rotated are the fuel
Every one of these logins started with a password the attacker already held. Huntress found the campaign replaying username and password pairs from earlier breaches that were never changed. That detail connects this to a supply line we keep writing about. Infostealers like the Djinn stealer raiding cloud and developer keys and the credential-theft networks behind Stealc and Amadey exist to fill exactly these lists. A stolen password sitting unused in a dump is worthless until a spray like this one tries it. Rotation is not hygiene theater; an un-rotated breached password is a live key, and this campaign is what turns it.
It also rhymes with a pattern we flagged when attackers abused OAuth connected apps to reach Salesforce data: the identity layer fails not because the primary control is missing, but because a secondary, older, less-watched path never got the same scrutiny. Single sign-on and modern MFA get audited. The legacy grant type that predates them does not.
What to change this week
There is nothing to patch here, because there is no bug. This is a configuration and hygiene problem, and every fix is something you own.
-
Close the ROPC path. In Entra ID, turn on the requirement for strong client authentication, which refuses the ROPC flow outright. Microsoft exposes it as the userStrongAuthClientAuthNRequired setting. This single change would have blocked the campaign.
-
Audit your Conditional Access for gaps, do not just confirm it exists. A policy scoped to specific apps, specific users, or only untrusted locations is a policy with holes. Widen it to cover every user, every cloud app, and every client type, including the legacy and other-clients bucket where ROPC lands.
-
Restrict Azure CLI to the admins who need it. Most of your users never run it. Blocking the Azure CLI application for everyone else removes a first-party sign-in surface the attacker was counting on.
-
Rotate old passwords, starting with accounts that show up in breach data. If a credential predates your last forced reset, treat it as already known to attackers.
For detection, the sharpest hunt is a successful sign-in to the Azure CLI application recorded as single factor for a user who is enrolled in MFA, especially from an autonomous system you have never seen your staff use. That combination, a valid password plus no second factor plus foreign infrastructure, is the campaign's signature, and Huntress advises triaging by which credentials are still valid rather than by raw attempt volume. Detection watching Entra sign-in logs for that shape catches the successful logins even when the 81 million failures are only noise.
The lesson is not that MFA failed. It is that MFA enforced in one place is not MFA enforced everywhere, and attackers earn their keep by finding the one login path your policy forgot. The control you turned on is only as strong as the flows it actually covers. Go look at which ones yours does.