```html ```
top of page

81 Million Login Attempts Came Over IPv6. Then We Audited Our Own Signup Gate and Found It Cannot See IPv6 Either.

  • Writer: Patrick Duggan
    Patrick Duggan
  • 2 hours ago
  • 6 min read

Between June 12 and June 26, a password spray campaign against Azure CLI sign-ins generated more than 81 million login attempts. Huntress reported at least 78 compromised Microsoft accounts across 64 organizations before traffic from the identified range stopped on July 2. Password spraying overall is up 155x in the first half of 2026.


The campaign did not break MFA. That distinction is the whole lesson, and then it cost us something to learn it properly, because we went and checked our own front door and did not like what we found.


The Mechanism: A Deprecated Grant That Skips the Interactive Step



The attackers used the OAuth Resource Owner Password Credentials flow — ROPC — which Azure CLI still supports.


Modern OAuth is interactive by design. You get redirected to the identity provider, you authenticate in its context, and that is where Conditional Access evaluates the request and where MFA gets enforced. The interactive step is not a UX detail; it is the enforcement point.


ROPC skips it. An application hands a raw username and password straight to the token endpoint and receives access tokens back. No redirect, no interactive authorization, no place for an MFA challenge to happen.


So MFA was not bypassed, defeated, or phished. It was never invoked, because the code path the attacker chose does not contain it. Every one of those 64 organizations could have had MFA correctly enabled and still been standing in the exact spot they were standing in.


The gap that made it exploitable is Conditional Access scoping. CA policies apply to selected apps, users, locations and enforcement modes. Cover most of your surface and leave one deprecated grant on one legacy client uncovered, and your dashboard reports full compliance while an unauthenticated path stays open. The policy is not wrong. It is just not everywhere, and "not everywhere" is indistinguishable from "everywhere" on the summary screen.


The traffic came from an IPv6 range operated by hosting provider LSHIY LLC.


Hold onto that last detail.


So We Audited Our Own Registration Gate



We run a free API key registration for our threat feed, and it gets farmed. We built four gates against that: email deliverability checks, IP velocity limits per /24 and /16, a cloud-origin check on the registering ASN, and a burned-host rule that refuses anyone whose address or neighborhood previously had a key revoked for abuse.


They work. Our current state: 110 registrations, 34 revoked, 32 of those specifically for abuse. One farming operator ran 26 keys from a single ASN across six distinct /24s over three and a half weeks and every one of those 26 is now revoked. A plus-addressing trick — three keys minted in four minutes from one Gmail inbox using +tags, which our duplicate check saw as three different people — is closed.


Then we read the LSHIY detail from the Huntress reporting and went to look at the one thing we had never checked.


Our prefix function begins with this:


if the address contains a colon, return null.


IPv6 is skipped. Deliberately, with a comment explaining why — IPv4 /24 and /16 arithmetic is meaningless on a 128-bit address space, and rather than get it wrong we did not do it.


The consequence is that for an IPv6 registrant, all four gates are off. No velocity counting. No burned-host lookup. No burned-neighborhood lookup. And because we never stamped the origin ASN on those rows either, the cloud-origin check and the learned-ASN check are blind too.


25 of our 110 registrations came over IPv6. That is 23 percent of everyone who ever signed up, walking through a gate that was not looking.


It is not theoretical. The evidence was sitting in our own table:


One residential-scale /64 minted four separate keys. One was revoked. Two of the remaining ones are live and have run thousands of queries each. A /64 is a single customer premises — one household, one office. Under our IPv4 rules, key number four from one /24 trips the velocity limit. Over IPv6 nothing counted at all.


One IPv6 address was revoked for abuse on August 8. It is a burned host that cannot burn anything, because the burn lookup returns null before it ever runs. That address, and every address beside it, can register again tomorrow.


So: the exact address family the 81-million-attempt campaign chose to run on is the exact address family our gate does not inspect. That is not a coincidence and I do not think it is bad luck. IPv6 is where the cheap seats are, for the same reason in both cases — defensive tooling was written for IPv4 and quietly no-ops on everything else.




Why I Am Publishing Our Own Hole



Because a threat intel company that only publishes other people's failures is running a marketing operation, and because the finding is not embarrassing in the way it first looks.


The gate was not broken. It was partial, and it reported success on the part it covered. That is the same failure the 64 Azure organizations had. Their Conditional Access was not broken either — it covered most apps, most users, most flows, and reported compliant. The uncovered slice does not show up as a warning anywhere, because a control that is not evaluated does not produce a finding. It produces silence, and silence renders identically to safety.


This is the thing we say constantly and had to be reminded of by our own data: green is a claim, not evidence. Our abuse controls have a real, countable win in them — 32 abuse revocations, a 26-key farm fully shut down. Reading that number, you would conclude the gate works. The number is true. It is also the number from 77 percent of the traffic, and nothing in it told us about the other 23 percent, because the missing checks never ran and therefore never failed.


If you take one operational habit from this post, make it that: go and find which of your controls silently no-op on a subset of input. Not which ones fail — the failing ones are visible. The ones that return early are the expensive ones.


Standard places to look, all of which are the same bug wearing different clothes: IPv6 addresses in any IPv4-shaped logic, non-ASCII in anything doing string comparison, addresses behind a CDN or proxy where you are reading the wrong header, deprecated auth grants that skip your enforcement point, and any function whose first line is a guard clause returning null on input you assumed was rare.


What Both Fixes Look Like



For Entra and Azure CLI, the mitigations are known and the campaign has made them urgent. Block ROPC. Audit Conditional Access for gaps in app scope, user scope, and enforcement mode — specifically hunting for legacy and deprecated grants that fall outside policy rather than confirming that the policies you have are enabled. Those are different questions and only the second one is usually asked.


For us, the fix is to canonicalise IPv6 to /64 for the household-level velocity check and /48 for the provider-level one, run the burn lookup on both, and stamp the origin ASN on IPv6 registrations so the cloud-origin and learned-ASN gates have something to read. It is not a hard change. It is maybe thirty lines. It was simply never written, because IPv4 covered enough of the traffic to make the gate look like it worked.


That change touches the live registration path, so it goes through our deployment gate and does not ship on the strength of a blog post. When it is deployed and verified I will say so, with the before and after numbers, including if the numbers are boring.


The Uncomfortable Symmetry



Sixty-four organizations had MFA on and got compromised through a code path where MFA does not exist.


We had four abuse gates on and let through 23 percent of our signups through a code path where the gates return null.


Neither is a story about weak security. Both are stories about coverage being reported as a binary when it is actually a fraction, and about the missing fraction being invisible precisely because nothing in it ever throws an error.


The 155x increase in password spraying this year is not happening because attackers got smarter. It is happening because it still works, and it still works because the enforcement gaps are structural, boring, and do not appear on any dashboard as red.


Sources



The Hacker News, "Azure CLI Password Spray Hits at Least 78 Microsoft Accounts in 81M+ Attempts"


BleepingComputer, "Password spraying attacks surge 155x as hackers exploit MFA gaps"


TechRepublic, "Azure CLI Password Spray Attack Exposes Microsoft 365 MFA Gap"


Huntress research on the June 12–26, 2026 campaign


Our own registration table, audited August 19, 2026. Aggregate figures only — no registrant addresses, prefixes or identities are published here or anywhere else.




If you go looking for a silently no-opping control in your own stack on the back of this and you find one, tell me what it was. That is the most useful thing anybody could send me this week. Rate this post below.





Her name was Renee Nicole Good.


His name was Alex Jeffery Pretti.

 
 
 

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page