User-Level Application Consent
The Midnight Blizzard attacks against Microsoft (and others) are still in the news. Microsoft has provided a few more details of the attack path, which Andy Robbins of SpectreOps expanded into the best overall summary of the attack I’ve seen so far. If you haven’t read it, you should.
In short, the attack started with a password spray (which should have been easily detectable; Thijs Lecomte recently wrote about using Sentinel to detect similar attacks) against a test tenant. Once the attacker had compromised an account there, they were able to pivot into Microsoft’s production tenant and escalate their privileges. Rather than recapping Andy’s summary, I want to focus on one key step the attacker seems to have used: user-level application consent.
The Application Consent Process
For an application or service to log in to your tenant, in general, it needs two things to exist: an Entra ID application and an Entra ID service principal. The application object is present in the application’s home tenant, whether that’s your tenant or that of a service provider or vendor. The service principal exists in your tenant; in fact, there will be one unique service principal in every tenant that uses the application. The service principal has the role assignments and consent permissions that have been granted to the application through a process known as consent.
You’ve seen the consent flow if you’ve tried to use Graph Explorer, Apple’s built-in mail or calendar clients for macOS or iOS, or a host of other applications. The user gets a dialog showing what permissions the application is asking for and can either approve or deny this request. Figure 1 shows an example consent dialog for a user who’s requesting access to Graph Explorer in a tenant. The contents of the dialog vary according to who’s making the request (an ordinary user or a user with administrative rights) and what permissions are being requested. In this case, the user has admin rights so the consent dialog includes the “Consent on behalf of your organization” checkbox. When the user clicks the Accept button, the service principal is updated with the specified permissions.
When User Consent Goes Wrong
There are 3 scenarios of interest for application consent. The first is when a user is consenting to an application for their own use. This has become much more common now that a large number of end-user-facing applications use Graph APIs to access data in the service. For example, the Apple iOS mail and calendar apps, as well as the third-party Fantastical app, will all show the device user a consent dialog when adding an Exchange Online account. The user’s consent grants permissions to the application to act on behalf of the user, which Microsoft refers to as “delegated permissions.”
The second is when an administrator consents to an application for individual use, as when a developer consents to using Graph Explorer. The third is when an administrator consents to an application on behalf of the entire organization. This is the most far-reaching case because the administrator is approving any user in the tenant to use the application with the designated permission. It’s important to note that a user can never consent to grant more permissions than their own account has, so an ordinary user can’t grant administrative permissions directly.
In the Midnight Blizzard attack, it appears that the attackers were able to exploit the user consent flow by creating an account and giving it an elevated permission (probably AppRoleAssignment.ReadWrite.All according to SpectreOps), then using that account to register an application in Microsoft’s production tenant, followed by a pivot to using that application to create service principals through the consent mechanism that ended up with elevated permissions. Presumably, Microsoft’s security software would have alerted their security team to an administrative consent in the production tenant—something which should only happen very rarely, and only under certain easily-monitored circumstances. On the other hand, the proliferation of end-user-facing applications that can use OAuth authentication and Graph APIs means that an organization the size of Microsoft will, in the normal course of business, see tens of thousands of “ordinary” consent requests. It only takes one bad one to slip through to create a serious problem.
Solving the User Consent Problem
User consent is a great example of the tension between improved security and user convenience. Users want to be able to consent to applications they want to use without requiring help from centralized IT staff (and the IT team doesn’t want to be bothered with ongoing noise from these requests). That focus on convenience led to the Microsoft default of allowing users to consent to their own apps… which led to the Midnight Blizzard compromise. Stopping user consent would have broken the kill chain and stopped the attack.
The obvious suggestion that arises from this is to block users from consenting to applications on their own. This is draconian but effective. However, there are some intermediate options that may make more sense for your tenant, depending on how many users you have, how often they need to request permissions, and what other security mitigations you have in place.
To control user consent, you can choose between three options in the Entra ID admin center:
- “Allow user consent for apps” provides wide-open user consent. This is the default.
- “Do not allow user consent” does what the name says: it doesn’t remove existing consent, but it prevents users from consenting to new applications.
- “Allow user consent for apps from verified publishers”: In this mode, your users can consent only to applications that were published by a verified publisher, or for your own internal apps, and then only for permissions that you’ve classified as “low impact”.
If these controls aren’t granular, you can also choose to allow administrators to review and approve individual consent requests from users. In this flow, when the user does something that would normally trigger a consent request, instead they see a dialog asking for a justification for the consent, which is then forwarded to the tenant’s administrators for review and approval. Microsoft refers to this process as the admin consent workflow, and it provides a decent balance between convenience and security, provided that your admins have the time and knowledge to review the requests instead of just smashing the “approve” or “reject” buttons.
Cleaning Up
You should already be regularly reviewing what Entra ID apps exist in your tenant and what permissions they have. One quick way to improve your security is to disable user consent (to stop any further incursions while you figure out what’s what) and then audit the existing suite of applications to look for anything that doesn’t belong. Once you’re satisfied that you understand what applications have what permissions, and that those permissions are appropriate, you can enable the admin consent workflow so that users can request applications they need access to.
The Storm-0558 and Midnight Blizzard attacks have two key things in common: they were both mounted by nation-states, and they both exploited Entra ID application permissions to give the attacker elevated access. We’ll see more attacks like this in the future, and it pays to be prepared.