all posts

Windows Said Yes When Microsoft Said No

I typed my password wrong. Microsoft's cloud identity system rejected it. Windows let me in anyway. This is not a vulnerability -- it is two Windows features interacting in an undocumented way that most administrators have not consciously accepted, producing a detection gap that defenders cannot currently close. Here is what it is, how it works, and what to do about it.

I want to be upfront about something before we get into it: I am a cybersecurity researcher. I am not a Windows internals expoert. I do not have a long list of CVEs to my name. What I do have is a habit of pulling logs when something feels wrong, and on the morning of March 3rd, something felt wrong.

I had just unlocked my work laptop. I vaguely remembered Caps Lock being on. I typed my password the way I always do. It worked. I sat there for a second thinking, wait -- was my Caps Lock actually on, or did I imagine it? Then I opened Event Viewer, because that is apparently what I do instead of just getting on with my morning.

What I found sent me down a three week rabbit hole that ended with Microsoft closing my report as not a vulnerability. I think they are probably right, technically. What I also think is that the behavior I documented is real, undocumented, active by default on most Entra ID joined Windows 11 deployments, and creates a detection gap that most security teams have not accounted for. So I am writing about it anyway.

The Setup: What Should Happen

My work machine is an Entra ID joined Windows 11 laptop. Entra ID is Microsoft's cloud identity platform, formerly known as Azure Active Directory. On a machine like this, when you type your password, it does not get checked locally against some file on the hard drive. It gets sent to Microsoft's cloud, the cloud says yes or no, and Windows acts accordingly. That is the whole point of cloud identity management.

There is a backup mechanism called the local credential cache. It stores a verifier of your last successful password so you can log in on a plane with no internet. It is designed for offline scenarios. When you are sitting at a desk in an office with a 75 millisecond round trip to Microsoft's authentication servers, the credential cache is not supposed to be doing anything interesting.

The authentication flow for a normal login looks roughly like this:

  1. You type your password and hit enter
  2. The Negotiate package tries local/Kerberos auth first, fails immediately because your account is cloud-only
  3. CloudAP picks up the credential and sends it to Entra ID
  4. Entra says yes or no
  5. Windows grants or denies the session accordingly
  6. Your PRT (Primary Refresh Token) silently obtains access tokens for Teams, Outlook, etc.
  7. DUO fires as a second factor

Clean. Simple. The cloud is authoritative. That is the design.

The Logs That Started This

Here is what Event Viewer showed me from that morning:

09:44:45  Event 4625 - Login FAILED
          SubStatus: 0xC000006A  (correct username, wrong password)
          Caller:    C:\Windows\System32\LogonUI.exe
          PID:       0x7864

09:44:49  Event 4648 - Explicit credential resubmission
          Caller:    C:\Windows\System32\LogonUI.exe
          PID:       0x7864  ← same process, no new input

09:44:49  Event 4624 - Login SUCCESS
          Correlation ActivityID matches the 4648 above

The 4625 is a login failure. SubStatus 0xC000006A specifically means correct username, wrong password -- not a network issue, not MFA, not an expired account. The password was wrong. Then, four seconds later, a 4648 fires. Event 4648 is defined by Windows as "a logon was attempted using explicit credentials." The caller is LogonUI.exe, the same process, the same process ID. And then a 4624 -- login success -- sharing an Activity GUID with the 4648, meaning Windows itself is linking the retry and the success as one logical operation.

Nobody typed anything. The same process submitted credentials twice. The second one worked.

I filed a report with MSRC.

Microsoft's First Response

MSRC reviewed it and told me, in polite but clear terms, that the behavior I described did not meet their vulnerability bar because it aligned with intended authentication handling. They asked me to provide additional evidence demonstrating a genuine security boundary violation.

Fair enough. I went back to the logs.

A few days later it happened again, on March 9th, during a cold boot this time. Same sequence. Different PID, same pattern. I pushed back with the PID match evidence and the Activity GUID correlation, and asked them directly: if LogonUI does not compensate for keyboard state, what is the 4648?

Their response included a statement to the effect that LogonUI's role is limited to collecting user input and passing it to the authentication stack, and that it does not normalize, compensate, or make adjustments for keyboard state during password entry. No retry behavior. No case correction. Not by design, not at all.

My logs showed the same LogonUI.exe PID on the failure and the retry, with no user input between them. Their statement said it does not do that. One of those things had to be wrong. I was fairly confident it was not my logs.

A Good Alternative Explanation (and Why It Didn't Hold)

I sent everything to a friend who knows Windows authentication better than I do. They looked at it for about ten minutes and said, essentially, that I had misread the 4625.

Their argument was reasonable: on an Entra ID joined machine with a cloud-only account, the Negotiate package fails immediately when it sees your account. It does not know what to do with a cloud user, so it passes the credential off to CloudAP. The 4625 is just noise from the Negotiate handoff, not evidence that the password was actually wrong. The 4648 is CloudAP picking up the correct password and successfully authenticating against Entra. Everything is fine. Nothing to see here.

I had to admit this was a plausible alternative explanation. If correct, it would mean the whole thing was normal behavior and I had been chasing a ghost for two weeks.

So I pulled the Entra ID sign-in logs.

The Entra Logs

If the alternative explanation was correct, Entra would show a successful Windows Sign In event around 9:44 AM on March 3rd and 9:00 AM on March 9th. CloudAP sends the correct password, Entra says yes, done.

What I found instead:

March 3, 2026 — 9:44:45 AM MDT
Application:    Windows Sign In
Status:         FAILURE
Error code:     50126
Failure reason: Error validating credentials due to invalid username or password
User agent:     Windows-AzureAD-Authentication-Provider/1.0
Latency:        62ms

-- no success entry anywhere near this timestamp --

March 9, 2026 — 9:00:55 AM MDT
Application:    Windows Sign In
Status:         FAILURE
Error code:     50126
Failure reason: Error validating credentials due to invalid username or password
User agent:     Windows-AzureAD-Authentication-Provider/1.0
Latency:        81ms

-- no success entry anywhere near this timestamp --

Error 50126 means the password hash did not match. That is Entra's definitive no. The user agent Windows-AzureAD-Authentication-Provider/1.0 is CloudAP -- so my friend was right that CloudAP sent the credential. Wrong about what Entra said back. The latency of 62ms and 81ms confirms these were real cloud round trips. The machine was online. Entra was reachable. And Entra said the password was wrong.

Then, four to five seconds later, LSASS logged a successful session. With no corresponding Entra success entry on either date.

The alternative explanation required step three -- CloudAP sends the correct password, Entra says yes -- to succeed. It did not succeed. Entra said no. Windows said yes anyway.

Ruling Out Every Alternative

At this point I had a finding that looked serious but I wanted to poke every hole I could before drawing conclusions. The obvious candidates:

The PRT (Primary Refresh Token)

The PRT is a long-lived token that lets apps silently get cloud access without re-prompting for your password. Could it have silently authorized the Windows session after Entra's rejection? I pulled the non-interactive sign-in logs, which is where PRT-based silent auth shows up. The pattern was identical on both dates: PRT activity stops when the screen locks, Entra rejects the password, Windows grants a local session, then the PRT resumes and starts getting tokens for Teams and Outlook. The PRT was not involved in the session authorization -- it kicked back in afterward.

If the PRT had authorized the session, there would be a successful Windows Sign In entry with Incoming token type: primaryRefreshToken in the Entra interactive logs. There is not one on either date.

On-premises Kerberos or Enterprise PRT

My org has domain controllers at every plant. Could some hybrid auth path have stepped in? I ran dsregcmd /status on the machine:

AzureAdPrt         : YES
EnterprisePrt      : NO
OnPremTgt          : NO
CloudTgt           : YES  (scoped to .windows.net/.azure.net only)

No enterprise PRT. No on-premises Kerberos ticket. The only authentication mechanisms available on this machine for interactive logon are Entra ID and the local credential cache. Entra rejected the credential. That leaves the credential cache.

The LogonType 11 Question

When I dug into the raw XML of the event logs, I found that the successful 4624 events on March 3rd included one with LogonType: 11 -- cached credentials -- from svchost.exe, in addition to the LogonType 2 interactive session from LogonUI. I got briefly excited thinking I had confirmed the cache as the mechanism.

Then I checked a completely normal login. Same LogonType 11 from svchost. It fires on every unlock. It is background noise, not evidence of an anomaly. March 9th, notably, has no LogonType 11 at all -- just a clean LogonType 2 interactive success from LogonUI after the Entra rejection. No cached credential background session. No alternative explanation. That is the cleanest of the three incidents.

The Controlled Reproduction

On March 26th I deliberately reproduced the behavior with Procmon running, capturing all LogonUI.exe and Winlogon.exe activity. The Entra logs showed another 50126 failure at 10:01:30 AM. LSASS showed the same 4625 to 4648 to 4624 sequence. And the Procmon capture showed something I had been trying to confirm for three weeks:

No keyboard input events occurred during the five second gap between the failure and the retry.

What LogonUI was doing during those five seconds was loading credential provider DLLs. DuoCredProv.dll, DuoCredFilter.dll, credprovs.dll, aadauthhelper.dll, all the credential provider components initializing. That is automated initialization work, not a user retyping a password. LogonUI then exited with status 0 -- success -- seventeen seconds after Entra had returned error 50126.

I also noticed, while reviewing the Procmon capture, that LogonUI made a TCP connection to DUO's infrastructure during this sequence. DUO fires from inside the LogonUI process context, which means LogonUI informed DUO of a successful authentication. DUO independently confirmed a session nine seconds after the LSASS 4624 on both March 3rd and March 9th.

note

Procmon does not capture kernel-level secure input processing -- the lock screen keyboard path runs at a privileged level specifically to prevent credential theft. So the absence of keyboard events is meaningful but not a mathematical proof. The DLL loading sequence and the LogonUI exit status are the stronger indicators.

What the Evidence Shows

Across five independent data sources, on three separate dates, the story is consistent: Entra ID explicitly rejected the credential. Windows granted an interactive session anyway. The local credential cache is the mechanism. The PRT then silently restored full cloud application access, making the anomaly invisible in normal use.

The specific security concern here is not just that the credential cache can override a live cloud rejection -- it is that an attacker with a case-incorrect stolen credential produces logs that are completely indistinguishable from a legitimate user who made a Caps Lock mistake. Same Entra 50126. Same LSASS sequence. Same PRT resumption. No field anywhere in any of these events distinguishes the two scenarios. A defender cannot tell them apart.

Microsoft Defender for Endpoint (SenseIR.exe) was active on the machine on all three dates. It did not flag any of the incidents.

Is This Actually a Vulnerability?

I reported it to MSRC and they closed it as not meeting their vulnerability bar. Having thought about it for a while, I think their position is defensible -- and I also think it misses the point.

The credential cache is working as designed. Microsoft built it. They know it exists. They know it can authorize sessions when Entra rejects a credential. They chose not to restrict it on connected machines by default, and administrators who want strict cloud-only authentication can disable it with a single policy setting. From that angle, this is a configuration issue, not a product defect.

The problem is that the interaction between the credential cache and live cloud identity rejection is not documented anywhere I can find. Administrators running Entra ID joined fleets have not been told this is a thing that can happen. The default configuration permits it. And the resulting log sequence is forensically indistinguishable from a legitimate login -- which means even if you know about it, you cannot reliably detect it after the fact.

That combination -- undocumented, active by default, undetectable -- is worth talking about regardless of what you call it. So let us talk about what to actually do.

What Teams Can Do

Disable the credential cache entirely

This is the correct answer for any Entra ID only environment where users do not have a legitimate offline login use case. If your users are never going to need to log into their laptops on a plane with no internet, there is no reason to keep the credential cache enabled.

# Group Policy:
Computer Configuration > Windows Settings > Security Settings >
Local Policies > Security Options >
"Interactive logon: Number of previous logons to cache"
Set value to: 0

# Or via registry:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
CachedLogonsCount = REG_SZ "0"

# Or via Intune (Settings Catalog):
Search: "Interactive logon: Number of previous logons to cache"
Set to: 0

With CachedLogonsCount at zero, a live Entra rejection is final. No local fallback. No cached credential to present. The behavior I documented cannot happen.

Enforce Conditional Access on every interactive sign-in

A strict Conditional Access policy requiring a compliant device token and MFA on every authentication limits what an attacker can do even if they get a local session via the cache. The locally-authorized session does not automatically satisfy Conditional Access evaluation for subsequent cloud app access -- the PRT still needs to obtain tokens, and those requests can be blocked at the CA layer if the session does not meet policy.

This does not prevent the local session from being granted. It limits the blast radius.

Use FIDO2 or Windows Hello for Business

Passwordless authentication eliminates the underlying condition entirely. If there is no password being submitted, there is no wrong-case credential for the cache to accept. Windows Hello for Business uses asymmetric cryptography tied to the device and a PIN or biometric -- the local credential verifier is completely different and the Caps Lock vector does not apply. This is the long-term answer for any organization taking Entra ID seriously.

Build the detection rule

If you cannot immediately change the cache policy, the indicator to watch for is: a 50126 failure in Entra ID interactive sign-in logs on a given machine, with no corresponding Entra success entry, followed by a LSASS 4624 on that same machine within 30 seconds. That is the exact signature of this behavior. It is automatable in Microsoft Sentinel:

// Sentinel KQL -- Entra rejection with local session success
let EntraFailures = SigninLogs
    | where ResultType == "50126"
    | where AppDisplayName == "Windows Sign In"
    | project FailTime = TimeGenerated, DeviceName = DeviceDetail.displayName, UPN = UserPrincipalName;
let LocalSuccess = SecurityEvent
    | where EventID == 4624
    | where LogonType == 2 or LogonType == 11
    | where ProcessName has "LogonUI"
    | project SuccessTime = TimeGenerated, Computer, Account;
EntraFailures
| join kind=inner LocalSuccess on $left.DeviceName == $right.Computer
| where SuccessTime between (FailTime .. (FailTime + 30s))
| project FailTime, SuccessTime, DeviceName, UPN, Account

You will also want to alert on LogonType 11 in interactive 4624 events on Entra ID joined machines in general. On an online machine, cached credentials should not be the mechanism for a primary interactive session. If you see it, something used the cache when it probably should not have.

What Microsoft Said

I submitted three rounds of evidence over three weeks. The position communicated back was that this does not meet the MSRC vulnerability bar, and that the behavior aligns with intended authentication handling. They pointed at the cache configuration as the appropriate mitigation path, which is not wrong.

What I pushed back on, and never got a satisfying answer to, was the specific claim that LogonUI does not compensate for keyboard state during password entry. The logs show the same LogonUI.exe process ID on the failure and the retry, no keyboard input between them, and LogonUI exiting with status 0 after Entra's rejection. Whatever is happening in that five second gap, it is not the user retyping their password. The case was closed without addressing that specific contradiction.

I am not particularly bitter about it. MSRC reviews a lot of reports and the bar for what constitutes a security boundary violation is genuinely high. I do think the forensic indistinguishability point deserved more engagement than it got. An attacker path that produces logs identical to a legitimate login is a defender problem even if it is not a product bug.

A Note on Windows Authentication Logging

Something I did not fully appreciate before this investigation is how genuinely difficult it is to piece together what happened during a Windows login event. Not because the logs are sparse -- they are not, there are a lot of them -- but because the relevant information is scattered across at least three completely separate systems that do not speak to each other, use different timestamps, and require different access levels to retrieve.

Your LSASS Security event log lives on the machine. Your Entra ID sign-in logs live in the cloud portal. Your non-interactive sign-in logs are a separate tab in that same portal that most people never open. Your PRT state lives in dsregcmd output. Your credential provider behavior lives in a Procmon capture you have to deliberately set up in advance. Your DUO events live in a completely separate vendor dashboard. None of these things talk to each other by default. There is no single place you can go and see what actually happened during a login.

This matters a lot for the specific finding I documented here. The behavior I observed -- Entra rejecting a credential and Windows granting a session anyway -- is only visible if you correlate the LSASS event log with the Entra interactive sign-in log and notice that there is no success entry to match the local 4624. If you are only watching one of those two systems, you miss it entirely. If you are watching the LSASS logs and see a 4625 followed by a 4624, you might assume the user just retyped their password. If you are watching the Entra logs and see a 50126 failure with nothing after it, you might assume the user gave up and came back later.

The non-interactive logs made this even worse. PRT-based silent token acquisition, which is what restored my cloud application access after the anomalous session, shows up in a completely different view of the Entra portal. Most detection logic I have seen does not correlate interactive and non-interactive logs together. The result is that an authentication event that bypassed cloud identity verification and then silently restored full application access looks, from any single vantage point, like either a failed login or a normal PRT refresh.

I do not think this is entirely Microsoft's fault. Distributed authentication across cloud and local components is genuinely hard to instrument. But the current state of Windows authentication logging is not good enough for the threat model that Entra ID joined deployments are supposed to address. If your identity provider is in the cloud, your authentication visibility needs to be in the cloud too, correlated end to end. Right now it is not.

What I Learned

I started this investigation thinking I had found a secret Caps Lock retry feature baked into LogonUI. By the end I had learned that my friend was partially right, that the 4625 on Entra ID joined machines is often just Negotiate handoff noise, that the PRT and the local session are two completely separate things that I had conflated for a week, and that five independent data sources all telling the same story is genuinely compelling even when you cannot reproduce the behavior on demand.

I also learned what responsible disclosure actually feels like in practice, which is mostly waiting and rewriting the same evidence in increasingly precise language, hoping someone reads it properly. And I learned that "not a vulnerability" and "not worth knowing about" are two different things, and that the security community sometimes needs the second conversation even when the first one is settled.

The credential cache overriding a live Entra rejection is real. The forensic indistinguishability is real. The default configuration permits it on most Entra ID joined deployments. You can close it in about five minutes with a policy change, and you probably should.


The full technical report and evidence documentation are available on request. Questions or corrections: hello@nyxvia.com