This isn't a zero-day vulnerability hidden deep in kernel internals. Instead, it's a silent OAuth bypass triggered by a seemingly innocuous detail, having a non-Chrome browser set as your default for M365 on Android. If you're using browsers like Edge or Firefox, this could have affected you.


The Prerequisites

Let's quickly cover the basics, OAuth 2.0 allows applications to gain authorized access to resources without ever handling your credentials. You log in via a browser, and the application receives a one-time code when you're redirected back to http://localhost:{port}. Simple. Now, consider prompt=none. This OAuth parameter instructs the authorization server to not display a login screen if the user is already authenticated, allowing the application to silently obtain tokens with no interactions. While useful for seamless user experiences, it introduces a significant attack surface if proper checks aren't rigorously enforced. The standard authentication flow does include a check for the Chrome Android user agent. However, what happens when you're not using Chrome? If you use a browser like Edge, for example (which will prompt you to authenticate to M365 by default), you could effectively bypass this user agent check and an attacker could intercept the authentication token.

The Exploitation

Here’s how this vulnerability could have been exploited:

  1. You are logged in to M365 in any browser except Chrome as your default on Android (e.g., Edge).
  2. An app with malicious intent is installed on your phone (either sideloaded or disguised as legitimate).
  3. That app initiates an OAuth request using a Microsoft client_id that trust localhost as a redirect such as 04b07795-8ddb-461a-bbee-02f9e1bf7b46 (Microsoft Azure CLI) or fb78d390-0c51-40cd-8e17-fdbfab77341b (Exchange REST API), and sets the redirect_uri to http://localhost, with the prompt=none parameter included.
  4. If Edge is installed, the attacker can force the request through the Edge app using the microsoft-edge:// scheme, effectively piggybacking on your existing authenticated session.
  5. The browser silently redirects to localhost with an authentication code in the URL.
  6. A small JavaScript payload on the app controlled localhost page or raw socket captures this code and transmits it back to a attacker controlled server.

Zero user interaction was required. You never saw a login page, nor did you enter credentials. And now an attacker has a valid authentication token, giving them privileged access to your account and to do any action you could, read data, write data, even delete everything if you have this access.

Disclosure and Remediation

This vulnerability was reported to the Microsoft Security Response Center (MSRC) in early 2025. After a few hiccups and an initial "out of scope" classification, I was able to make my case heard, and the report was reopened for a fix. Following this detailed second review, MSRC identified and remediated the underlying vulnerability in the authentication flow. The fix was confirmed and deployed on October 8, 2025. I would like to thank the MSRC team for their investigation, remediation, and for the bounty awarded for this finding.

Key Lessons

If you're building mobile authentication flows, here’s what needs to change:
  • Never trust untrusted redirects. Do not allow redirection to a non-HTTPS, untrusted URI without explicit user interaction.
  • Disallow prompt=none for localhost. Force user interaction for localhost redirects, especially if they are unauthenticated.
  • Validate redirect URIs rigorously. Treat redirect URI validation as a critical security control, as your tokens depend on it.

Proof.mp4

Click me for proof