Originally published with NVISO · · 9 min.

Detecting & Preventing Rogue Azure Subscriptions

A few weeks ago, NVISO observed how a phishing campaign resulted in a compromised user creating additional attacker infrastructure in their Azure tenant. While most of the malicious operations were flagged, we were surprised by the lack of logging and alerting on Azure subscription creation.

Creating a rogue subscription has a couple of advantages:

  • By default, all Azure Active Directory members can create new subscriptions.
  • New subscriptions can also benefit from a trial license granting attackers $200 worth of credits.
  • By default, even global administrators have no visibility over such new subscriptions.

In this blog post we will cover why rogue subscriptions are problematic and revisit a solution published a couple of years ago on Microsoft’s Tech Community. Finally, we will conclude with some hardening recommendations to restrict the creation and importation of Azure subscriptions.

The deployments and recommendations discussed throughout this blog post require administrative privileges in Azure. As with any administrative actions, we recommend you exercise caution and consider any undesired side-effects privileged changes could cause.

With the above warning in mind, global administrators in a hurry can directly deploy the logging of available subscriptions (and reading the hardening recommendations)…

Deploy to Azure
Figure 1: Deploy to Azure

Azure’s Hierarchy

To understand the challenges behind logging and monitoring subscription creations, one must first understand how Azure’s hierarchy looks like.

In Azure, resources such as virtual machines or databases are logically grouped within resource groups. These resource groups act as logical containers for resources with a similar purpose. To invoice the usage of these resources, resource groups are part of a subscription which also defines quotas and limits. Finally, subscriptions are part of management groups which provides centralized management for access, policies or compliance.

Management levels and hierarchy in “Organize your Azure resources effectively” on docs.microsoft.com.
Figure 2: Management levels and hierarchy in “Organize your Azure resources effectively” on docs.microsoft.com.

Most Azure components are resources as is the case with monitoring solutions. As an example, creating an Azure Sentinel instance will require the prior creation of a subscription. This core hierarchy of Azure implies that monitoring and logging is commonly scoped to a specific set of subscriptions as can be seen when creating rules.

Alert rules and their scope selection limited to predefined subscriptions in the Azure portal.
Figure 3: Alert rules and their scope selection limited to predefined subscriptions in the Azure portal.

This Azure hierarchy creates a problem of the chicken or the egg: monitoring for subscription creations requires prior knowledge of the subscription.

Another small yet non negligible Azure detail is that by default even global administrators cannot view all subscriptions. As detailed in “Elevate access to manage all Azure subscriptions and management groups”, viewing all subscriptions first requires additional elevation through the Azure Active Directory properties followed by the unchecking of the global subscription filter.

The Azure Active Directory access management properties (left) and global subscriptions filter enabled by default in the Azure portal (right).
Figure 4: The Azure Active Directory access management properties (left) and global subscriptions filter enabled by default in the Azure portal (right).

The following image shows the view prior (left) and after (right) the above elevation and filtering steps have been taken.

Subscriptions before (left) and after (right) access elevation and filter removal in the Azure portal.
Figure 5: Subscriptions before (left) and after (right) access elevation and filter removal in the Azure portal.

In the compromise NVISO observed, the rogue subscriptions were all named “Azure subscription 1”, matching the default name enforced by Azure when leveraging free trials (as seen in the above figure).

Detecting New Subscriptions

A few years ago a Microsoft’s Tech Community blog post covered this exact challenge and solved it through a logic app. This following section revisits their solution with a slight variation using Azure Sentinel and system-assigned identities. Through a simple logic app, one can store the list of subscriptions in a log analytics workspace for which an alert rule can then be set up to alert on new subscriptions.

Deploy to Azure
Figure 6: Deploy to Azure

Collecting the Subscription Logs

The first step in collecting the subscription logs is to create a new empty logic app (see the “Create a Consumption logic app resource” documentation section for more help). Once created, ensure the logic app has system-assigned identity enabled from its identity settings.

A logic app’s identity settings in the Azure portal.
Figure 7: A logic app’s identity settings in the Azure portal.

To grant the logic app reader access to the Azure Management API, go to the management groups and open the “Tenant Root Group”.

The management groups in the Azure portal.
Figure 8: The management groups in the Azure portal.

Within the “Tenant Root Group”, open the access control (IAM) settings and click “Add” to add a new access.

The tenant root group’s access control (IAM) in the Azure portal.
Figure 9: The tenant root group’s access control (IAM) in the Azure portal.

From the available roles, select the “Reader” role which will grant your logic app permissions to read the list of subscriptions.

A role assignment’s role selection in the Azure portal.
Figure 10: A role assignment’s role selection in the Azure portal.

Once the role selected, assign it to the logic app’s managed identity.

A role assignment’s member selection in the Azure portal.
Figure 11: A role assignment’s member selection in the Azure portal.

When the logic app’s managed identity is selected, feel free to document the role assignment’s purpose and press “Review + assign”.

A role assignment’s member selection overview in the Azure portal.
Figure 12: A role assignment’s member selection overview in the Azure portal.

With the role assignment performed, we can move back to the logic app and start building the logic to collect the subscriptions. From the logic app’s designer, select a “Recurrence” trigger which will trigger the collection at a set interval.

An empty logic app’s designer tool in the Azure portal.
Figure 13: An empty logic app’s designer tool in the Azure portal.

While the original Microsoft Tech Community blog post had an hourly recurrence, we recommend to lower that value (e.g. 5 minutes or less, the fastest interval for alerting) given we observed the subscription being rapidly abused.

A recurrence trigger in a logic app’s designer tool.
Figure 14: A recurrence trigger in a logic app’s designer tool.

With the trigger defined, click the “New step” button to add an operation. To recover the list of subscriptions search for, and select, the “Azure Resource Manager List Subscriptions” action.

Searching for the Azure Resource Manager in a logic app’s designer tool.
Figure 15: Searching for the Azure Resource Manager in a logic app’s designer tool.

Select your tenant and proceed to click “Connect with managed identity” to have the authentication leverage the previously assigned role.

The Azure Resource Manager’s tenant selection in a logic app’s designer tool.
Figure 16: The Azure Resource Manager’s tenant selection in a logic app’s designer tool.

Proceed by naming your connection (e.g.: “List subscriptions”) and validate the managed identity is the system-assigned one. Once done, press the “Create” button.

The Azure Resource Manager’s configuration in a logic app’s designer tool.
Figure 17: The Azure Resource Manager’s configuration in a logic app’s designer tool.

With the subscriptions recovered, we can add another operation to send them into a log analytics workspace. To do so, search for, and select, the “Azure Log Analytics Data Collector Send Data” operation.

Searching for the Log Analytics Data Collector in a logic app’s designer tool.
Figure 18: Searching for the Log Analytics Data Collector in a logic app’s designer tool.

Setting up the “Send Data” action requires the target Log Analytics’ workspace ID and primary key. These can be found in the Log Analytics workspace’s agents management settings.

A log analytics workspace’s agent management in the Azure portal.
Figure 19: A log analytics workspace’s agent management in the Azure portal.

In the logic app designer, name the Azure Log Analytics Data Collector connection (e.g.: “Send data”) and provide the target Log Analytics’ workspace ID and primary key. Once done, press the “Create” button.

The Log Analytics Data Collector’s configuration in a logic app’s designer tool.
Figure 20: The Log Analytics Data Collector’s configuration in a logic app’s designer tool.

We can then select the JSON body to send. As we intend to store the individual subscriptions, look for the “Item” dynamic content which will contain each subscription’s information.

The Log Analytics Data Collector’s JSON body selection in a logic app’s designer tool.
Figure 21: The Log Analytics Data Collector’s JSON body selection in a logic app’s designer tool.

Upon selecting the “Item” content, a loop will automatically encapsulate the “Send Data” operation to cover each subscription. All that remains to be done is to name the custom log, which we’ll name “SubscriptionInventory”.

The encapsulation of the Log Analytics Data Connector in a for-each loop as seen in a logic app’s designer tool.
Figure 22: The encapsulation of the Log Analytics Data Connector in a for-each loop as seen in a logic app’s designer tool.

Once this last step configured, the logic app is ready and can be saved. After a few minutes the new custom SubscriptionInventory_CL table will get populated.

Alerting on New Subscriptions

While collecting the logs was the hard part, the last remaining step is to create an analytics rule to flag new subscriptions. As an example, the following KQL query identifies new subscriptions and is intended to run every 5 minutes.

let schedule = 5m;
SubscriptionInventory_CL
| summarize arg_min(TimeGenerated, *) by SubscriptionId
| where TimeGenerated > ago(schedule)

A slightly more elaborate query variant can take base-lining and delays into account which is available either packaged within the complete ARM (Azure Resource Manager) template or as a standalone rule template.

Once the rule deployed, new subscriptions will result in incidents being created as shown below. These incidents provide much-needed signals to identify potentially rogue subscriptions prior to their abuse.

A custom “Unfamiliar Azure subscription creation” incident in Azure Sentinel.
Figure 23: A custom “Unfamiliar Azure subscription creation” incident in Azure Sentinel.

To empower your security team to investigate such events, we do recommend you grant them with Reader rights on the “Tenant Root Group” management group to ensure these rights are inherited on new subscriptions.

Hardening an Azure Tenant

While logging and alerting are great, preventing an issue from taking place is always preferable. This section provides some hardening options that Azure administrators might want to consider.

Restricting Subscription Creation

Azure users are by default authorized to sign up for a cloud service and have an identity automatically be created for them, a process called self-servicing. As we saw throughout this blog post, this opens an avenue for free trials to be abused. This setting can however be controlled by an administrator through the Set-MsolCompanySettings cmdlet’s AllowAdHocSubscriptions parameter.

AllowAdHocSubscriptions controls the ability for users to perform self-service sign-up. If you set that parameter to $false, no user can perform self-service sign-up.

Source: Microsoft

As such, Azure administrators can prevent users from singing up for services (incl. free trials), after careful consideration, through the following MSOnline PowerShell command:

Set-MsolCompanySettings -AllowAdHocSubscriptions $false

Restricting Management Group Creation

Another Azure component users should not usually interact with are management groups. As stated previously, management groups provide centralized management for access, policies or compliance and act as a layer above subscriptions.

By default any Azure AD security principal has the ability to create new management groups. This setting can however be hardened in the management groups’ settings to require the Microsoft.Management/managementGroups/write permissions on the root management group.

The management groups settings in the Azure portal.
Figure 24: The management groups settings in the Azure portal.

Restricting Subscriptions from Switching Azure AD Directories

One final avenue of exploitation which we haven’t seen being abused so far is the transfer of subscriptions into or from your Azure Active Directory environment. As transferring subscriptions poses a governance challenge, the subscriptions’ policy management portal offers two policies capable of prohibiting such transfers.

We highly encourage Azure administrators to consider enforcing these policies.

The subscriptions’ policies in the Azure portal.
Figure 25: The subscriptions’ policies in the Azure portal.

Conclusions

In this blog post we saw how Azure’s default of allowing anyone to create subscriptions poses a governance risk. This weak configuration is actively being leveraged by attackers gaining access to compromised accounts.

We revisited a solution initially published on Microsoft’s Tech Community and proposed slight improvements to it alongside a ready-to-deploy ARM template.

Finally, we listed some recommendations to harden these weak defaults to ensure administrative-like actions are restricted from regular users.

Archived Building a Monitoring Stack Monitoring Azure Logging Proof-of-Concepts