Hunt for compromised Azure subscriptions using Microsoft Defender for Cloud Apps
Published Aug 24 2022 06:00 AM 20.9K Views

In our present threat landscape, attackers are constantly trying to compromise organizations, each with their own set of motives. They may want to compromise accounts credentials to later sell, utilize compromised users to spread phishing campaigns, or even utilize the computing resources of the organization to deploy and run their own tooling. In this blog, we will describe how attackers can compromise Azure subscriptions and use them for malicious activities. In addition, we will share how Microsoft Defender for Cloud Apps data can help hunt for these activities and how to mitigate the risk of compromised subscriptions.



Attack Scenarios

We will focus on 3 main scenarios of how a subscription can be compromised and/or hijacked. All the scenarios have shared similarities but also a few differences.


Scenario 1 – Compromised Admin

In this scenario, an attacker has compromised a user with sufficient permissions to create a new subscription and/or manage an existing one (owner/contributor over a subscription). The admin can be compromised through leaked credentials, guessing credentials, stolen tokens, etc.  We observed in most cases, the attackers are choosing to create a new subscription instead of using an existing one (we assume they are doing so in order to reduce the chances of being notices). By using the compromised user, the attacker can access the subscription through the portal and/or Azure Management API and deploy their resources; for example, deploying VM’s for crypto-mining activities.


Scenario 2 – Compromised Admin to External User

The first stage of this scenario is the same as the previous one. An attacker has compromised an admin user and used it to create and/or takeover an existing subscription. Unlike the first scenario, this time the attacker tries to be stealthier and create a persistence mechanism by inviting an external user to the tenant. Then, the attacker promotes the external user to privileged roles. At this point, the attacker is using the external user to perform actions in Azure.  In some cases, instead of inviting a guest account, the attackers are creating a new account in the tenant.


Let’s see the attack flow in a graph:

  • The attacker is inviting a guest account using the compromised admin



  • The attacker creates a new subscription using the compromised admin



  • The attacker promotes the external account to privileged role. Now, the attacker can manage the subscription and deploy resources using the external account.








Scenario 3 – Compromised Admin to External User to Subscription Hijacking

For this scenario, we first need to understand some basic concepts.


What is subscription transferring?

Organizations might have several Azure subscriptions. Each subscription is associated with a particular Azure Active Directory (Azure AD) directory (tenant). To make management easier, many of our current customers need to be able to transfer a subscription to a different Azure AD tenant. The subscription transformation can be completed through the azure portal:





Figure 1 - Changing a directory of Azure subscription through the portal


Several Azure resources have a dependency on a subscription or a tenant. When transferring subscriptions, some of the resources and RBAC assignments are affected. For example, when transferring the subscription, all the role assignments of the subscription are deleted as documented here.


Another important nuance is that while the subscription is being transferred to another tenant, the billing account will remain the same unless it is manually modified, meaning that while the subscription is managed by different, the original tenants’ billing account is charged:



Figure 2 - warning message as seen on Azure portal


How are attackers abusing the subscription transferring feature?

Attackers have been observed utilizing the subscription transferring feature to hijack subscriptions from compromised tenants by transferring these subscriptions to attacker-controlled tenants. After the attackers own the hijacked subscriptions, attackers can utilize the subscription to deploy new resources for malicious activities.


Attack flow in graphs:

  1. An attacker compromises a user inside the victim tenant with enough permissions to invite a guest user from the attacker-controlled tenant (usually Global Administrator but can also be other such as Guest Inviter or User Administrator).



  1. The attacker creates a new Pay-As-You-Go / Azure Student subscription, or utilizes an existing one, and associates it with either an existing billing account or a new billing account with new payment information.



  1. The attacker gives the newly added guest account an admin role over the subscription




  1. The attacker account transfers the subscription to the attacker-controlled tenant.



What makes this technique relatively stealthy is the fact that after the subscriptions move to a different tenant, all the activity logs of the hijacked subscriptions will be routed to the attacker tenant.


Let’s hunt with Microsoft Defender for Cloud Apps

After learning about the attack surface, let’s see how we can proactively hunt and detect potential subscription compromise attempts and their related events.



  • Defender for Cloud Apps configured in the environment with the following connectors enabled:
  • Microsoft 365 Defender onboarded.


Hunt for suspicious external accounts

2 of the 3 mentioned scenarios include inviting malicious external accounts.

First, Defender for Cloud Apps customers can monitor all external accounts using the Defender for Cloud Apps portal under “Investigate” -> “Users and accounts” and filter for “external users” and “show admins only”:





In addition, using ‘Advanced Hunting’ in M365D portal, we can hunt for users who added guest accounts to the tenants:



| where Timestamp > ago(7d)
| where ActionType == "Add user."
| where RawEventData.ResultStatus == "Success"
| where RawEventData has "guest" and RawEventData.ObjectId has "#EXT#"
| mv-expand Property = RawEventData.ModifiedProperties
| where Property.Name == "AccountEnabled" and Property.NewValue has "true"
| project Timestamp, AccountObjectId, AccountDisplayName, newGuestAccount = RawEventData.ObjectId, UserAgent



If we want to filter further, we can search for new guest accounts who have been promoted to certain AAD roles:



let Roles = pack_array("Company Administrator");
let newGuestAccounts = (
| where Timestamp > ago(7d)
| where ActionType == "Add user."
| where RawEventData.ResultStatus == "Success"
| where RawEventData has "guest" and RawEventData.ObjectId has "#EXT#"
| mv-expand Property = RawEventData.ModifiedProperties
| where Property.Name == "AccountEnabled" and Property.NewValue has "true"
| project CreationTimestamp = Timestamp, AccountObjectId, AccountDisplayName, newGuestAccount = RawEventData.ObjectId,newGuestAccountObjectId = tostring(RawEventData.Target[1].ID), UserAgent);
let promotedAccounts = (
| where Timestamp > ago(7d)
| where isnotempty(AccountObjectId)
| where ActionType == "Add member to role."
| where RawEventData.ResultStatus == "Success"
| where RawEventData has_any(Roles) 
| where RawEventData.Actor has "User"
| project PromoteTimestamp = Timestamp, PromotedUserAccountObjectId = tostring(RawEventData.Target[1].ID));
//join the two tables
| join promotedAccounts on $left.newGuestAccountObjectId == $right.PromotedUserAccountObjectId
| where PromoteTimestamp  > CreationTimestamp
| project CreationTimestamp, PromoteTimestamp, PromotedUserAccountObjectId, newGuestAccount, newGuestAccountObjectId



Hunt for suspicious Azure Resource Management (ARM) activities

One of the most common resources deployed by attackers in compromised subscriptions are virtual machines. Let’s hunt for a new external user who has deployed at least X virtual machines:



let threshold = 3;
let newGuestAccounts = (
| where Timestamp > ago(7d)
| where ActionType == "Add user."
| where RawEventData.ResultStatus == "Success"
| where RawEventData has "guest" and RawEventData.ObjectId has "#EXT#"
| mv-expand Property = RawEventData.ModifiedProperties
| where Property.Name == "AccountEnabled" and Property.NewValue has "true"
| project newGuestAccountObjectId = tostring(RawEventData.Target[1].ID)
| distinct newGuestAccountObjectId);
| where Timestamp > ago(7d)
| where isnotempty(toscalar(newGuestAccounts))
| where Application == "Microsoft Azure"
| where ActionType == "Validate Deployments"
| where RawEventData contains "createVm"
| where AccountObjectId in (newGuestAccounts)
| summarize VMCreationCount = count() by AccountObjectId
| where VMCreationCount > threshold



Next, we can search users who have high risk sign-in activity, according to AAD Identity Protection, and have created a resource group (which is indicator of subscription creation):



let riskyAzureSignIns = (
| where Timestamp > ago(7d)
| where ErrorCode == 0
| where Application == "Azure Portal"
| where RiskLevelAggregated == 100 or RiskLevelDuringSignIn == 100
| project AccountObjectId, RiskySignInTimestamp = Timestamp);
let resourceGroupCreation = ( 
| where Timestamp > ago(7d)
| where Application == "Microsoft Azure"
| where ActionType == "Write ResourceGroup"
| project AccountObjectId, ResourceGroupCreation = Timestamp);
//join the tables
| join resourceGroupCreation on AccountObjectId 
| where ResourceGroupCreation between (RiskySignInTimestamp .. (RiskySignInTimestamp + 12h))



Build Detections with Defender for Cloud Apps Custom Policies Engine

Defender for Cloud Apps enable you to create a custom activity policies and craft your own logic. With custom policies, we can build alerts and take automatic actions on those users. Let’s take a look at a few examples.


Custom Policy 1 – External user created virtual machine 

In this case, we want to create an alert and send email notification every time an external user has created a new virtual machine. We will define the severity as 'low' as we still want to track this activity while still being able to focus on higher severity alerts.




Custom Policy 2 – Risky IP used to create multiple Virtual Machine

Defender for Cloud Apps allows admins to work with IP ranges and tag them accordingly. Some of the IP addresses will be tagged as ‘Risky’ by threat intelligence sources. In the following policy, we will alert users who created virtual machines from a risky IP address.




Relevant Alerts

There following alerts could be related to the scenarios we described above, with the ones listed in bold being the most critical to monitor.


Defender for Cloud Apps Alerts

  • Multiple VM activities
  • Impossible travel activity
  • Activity from infrequent country
  • Activity from an anonymous proxy
  • Activity from a Tor IP address
  • Activity from a password-spray associated IP address


M365D Alerts

  • Suspicious role assignment in Azure subscription
  • Access elevation by risky user
  • Risky user created global admin


Prevention mechanisms


Mitigate risks of credential guessing attacks

We observed that almost all of the compromised admins in subscription compromised attacks did not have MFA enabled, which would have helped combat the most common initial access vector of guessing a user’s credentials. The following recommendations help significantly reduce the risk of credential guessing attacks.


Enable conditional access policies

Conditional access policies are evaluated and enforced every time an attacker attempts to sign-in. Organizations can protect themselves from attacks that leverage stolen credentials by enabling policies such as compliant devices or trusted IP address requirements.  In the above example, the attacker’s sign-ins were assessed as high risk. Conditional access can be used to block or require MFA for sign-ins that Azure AD Identity Protection detects are risky in real time.


Enable continuous access evaluation

Continuous Access evaluation (CAE) revokes access in real time when changes in user conditions trigger risks, such as when a user is terminated or moves to an untrusted location.


Let us know if you have any other hunting scenarios you'd be interested in seeing. We would love to hear your feedback!

Version history
Last update:
‎Oct 31 2022 06:17 AM
Updated by: