HNHacker News
TopNewBestAskShowJobs

wibartle

12 karma · joined October 30, 2026

submissionscomments
wibartle··on Azure's Weakest Link – Five Full Cross-Tenant Compromises
> I want to first say thanks for engaging with me on this issue so directly, much appreciated.

Of course :)

> As I said earlier - validating that the customer controls an azure user doesn't necessarily mean that they have permissions to grant admin consent.

I think what I'm trying to say is: you don't really care if they have permissions to grant admin consent - you care if admin consent was granted. In step4, you show the link to the admin consent flow, and if they are an admin, they click it, and if not, they click skip. In step5, you check if an admin consent is present. It might have come from the prior step, but it also might have come from something else - who cares.

> whether I couple it to Entra or not is not something you can rely on

I think, if we're talking about the Entra admin consent flow, some degree of Entra usage can be assumed. The admin consent flow requires an Entra identity.

> PKCE

PKCE would actually not be a solve here. I don't know why so many people think PKCE solves problems that it doesn't solve, but it's very a common thing. Using browser dev tools, I can just change the tenant parameter with the appropriate cookies/state in place, and PKCE would verify fine. Even with confidential client PKCE, it just becomes a confused deputy based on the cookies.

> a new API

If you want to verify whether admin consent is present, the existing [oauth2permissiongrant](https://learn.microsoft.com/en-us/graph/api/resources/oauth2...) API exposes that information. Though admittedly I think we've left that thing in beta for a decade.

Don't get me wrong, I see what you're saying. We could OpenID Connect-ify it and emit an id token that has some sort of claim xms_adminconsent=1, which could be verified by the app. Actually, that's what the v1 endpoint does if you use the deprecated prompt=admin_consent parameter (minus xms_adminconsent=1, whose lack caused its own set of headaches). Directionally though, we felt that separating the auth flows from the provisioning flows simplified them both, and building special auth-ified variations for every provisioning flow (not just admin consent, but also sign-up, and authorize customer support guy, and ...) was over coupling. It's just unnatural to overload the authentication stack with all those random responsibilities. Having bog-standard REST+JSON APIs (i.e. MS Graph) for provisioning flows (and maybe also a default guided user interface like /adminconsent) provides a clean separation.

Edit: I think, if I were to make a change, it'd be a very small change - renaming `tid` to `tid_hint`. It's already a common practice to pass `login_hint` as a user indicator between services - and nobody gets confused and thinks that user is signed in, just because you have their username in a form post parameter. Maybe the explicit signposting of "hint" does the trick, socially.

wibartle··on Azure's Weakest Link – Five Full Cross-Tenant Compromises
> Should I validate that the user who started the flow belongs to the same Azure tenant id?

Yeah. Basically, I'd expect most services are responding to the admin consent callback by saying "Admin consent flow complete. Redirecting to previous page" (usually some setup flow page). Then that page does whatever it normally does (presumably OpenID Connect sign-in with a cache in cookies / localStorage / sessionStorage) to authenticate the user.

So in a typical flow, the user is already signed in, and your service just reads their identity out of storage after the admin consent flow. If your service receives an unsolicited admin consent callback (and so there is no "previous page" and no user is signed in), I'd expect it goes to your service home page which either tries to sign the user in or shows an unauthenticated experience.

That's to say - most services setup flows are multi-step anyway. First the customer provides some information, then they sign a contract, then they pay, then they admin consent, then they configure your service's primary configuration. If admin consent is launced from /setupwizard/step4, then after admin consent, you go on to /setupwizard/step5. The normal thing step5 does (presumably, checking that the user is signed in, and then show the primary configuration user interface) should be good enough, just the same as if somebody randomly hit step5 without hitting step4 previously.

wibartle··on Azure's Weakest Link – Five Full Cross-Tenant Compromises
That's me. But I don't understand your comment. Could you expand on it, either in a reply or preferably by email or via an MSRC case? You can reply with the vuln number so I can fast route it.

> The callback you're redirecting the user to at the end of that flow is forgeable

The redirect_uri is checked against the app registration. There's a general OAuth problem called "open registration redirects" that applies here, about which we've [blogged](https://www.microsoft.com/en-us/security/blog/2026/03/02/oau...) - but frankly, none of the industry solves this well.

In general, "I can create a link that goes to malware via X endpoint" isn't very interesting - you can also just create a direct link to the malware. Web browsers won't reliably show you the real destination of a link anyhow and even if they did, it's mostly just us hackers who understand URLs at all.

> as long as a way to authenticate users exists.

I'm lost. Entra supports [OpenID Connect](https://learn.microsoft.com/en-us/entra/identity-platform/v2...) for authentication. Most portals that surface admin consent authenticate the user with an OpenID Connect request either immediately before or after the admin consent request.