In the ever-evolving landscape of cybersecurity, businesses face mounting challenges in protecting their critical assets from sophisticated threats. Managed Security Service Providers (MSSPs) have emerged as crucial partners, offering expertise and cutting-edge solutions to fortify defenses. In the MSSP (or MSP – Managed Service Provider) scenario, Azure Lighthouse plays a crucial role in providing MSSPs a straightforward way to establish visibility to Azure environments they are managing and operating. Even though it’s not the only lifeline to the Client’s environment it might be a good idea to detect changes in the delegations.

In this blog post, I’ll share insights on how you can track changes in the Azure Lighthouse (LH) delegations. Research for this blog post was made together with my colleague Juhana Jaakkola.

Azure Lighthouse Architecture

The following picture describes the overall architecture in the Azure Lighthouse scenario including access to M365 Defender and Privileged Access Group usage in case that’s the chosen path. Azure Lighthouse delegations can be set on the subscription or even resource group level. The best practice is to follow the least privilege model when implementing delegation. In the MSSP scenario (when Sentinel is in scope) permissions are usually set to the resource group level. If MSSP is providing services around Defender for Cloud things comes a bit more complicated because it’s a subscription-level service. But that’s totally another story and worth a blog post.

Picture – credits to Joosua Santasalo.

The Use Case

The service provider (MSSP/MSP) is keen to monitor possible changes (additions/deletions) in Azure Lighthouse delegations from environments to which they are providing services, such as MDR or XDR.

Scenarios:

  • Delegations might be removed unintentionally or by hypothetical rogue admin. This can result in MSSP losing visibility and access to the Client environment.
  • Delegation might be added by adversaries to get a foothold in the environment. In such case, the adversary already would have access to the MSSP/MSP environment and would like to extend their access to the final target (i.e. the Client environment).

Detection

To detect changes in Azure Lighthouse delegations there are several options available, and I will shed light on these in this chapter highlighting the pros and cons of all the options.

The activity itself is logged in the Tenant Root Management group Activity Log (Directory Log) on the MSSP side as well as in Azure Activity Log in the Client environment.

The Activity Log is enabled by default on every Azure subscription and cannot be disabled so there isn’t anything you need to do to see the necessary events on the Client side.

On the MSSP side, the same activity is seen on the Azure Root Management Group level (My Customers – Activity Log or Root Management Group directory Activity Log). Root Management Group events are not ingested through the ‘Azure Activity’ data connector to Sentinel and underlying Azure Log Analytics. Therefore, alternative methods need to be considered. I will present a few possible options to tackle this scenario in the following chapters.

Defender or Cloud Apps for help?

What’s pretty interesting is that the Directory logs events are seen in Microsoft Defender for Cloud Apps (MDA) which enables the possibility to use MDA raw data for detecting changes. Below you can find pictures of two different activities related to the matter – unregister & register tenants.

Side note: Prerequisite for this – You need to have MDA deployed and have enabled O365 & Azure data connectors (which I strongly encourage you to do if you still haven’t done so). In addition, push MDA raw data to Sentinel if you want to create detection on the Sentinel side.

Key takeaway

With MDA all necessary events are found from the MSSP side and you don’t need to do cross-tenant queries or rules on the Client side. Without MDA, it is possible to use the logs in the Client workspace, but visibility is lost if the delegation is removed.

Option 1 – Microsoft Sentinel or Azure Monitor

Use Azure Activity Log events to identify changes in Azure Lighthouse delegations. The rule based on the KQL query can be created on Sentinel (analytic rule) or in Azure Monitor (Alert). The query can be triggered in both the Client environment and the service provider side.

Pros:

  • Can be easily established
    • Directly from MSSP Sentinel as a cross-workspace query
  • Creating a Sentinel rule in the Client environment Sentinel workspace or in Azure Monitor (Monitor Alert)
  • Azure Lighthouse delegation additions or changes are visible (create/modify) as incidents or alerts depending on which mechanism you are using

Cons:

  • You can detect additions on LH but immediately when someone removes the access you will lose visibility to the Client side. That being said, you will not see the alerts created in a scenario where LH delegations are removed.
    • An exception for this is if you use a mechanism to send notifications (email, sms, Teams message, etc) outside of the environment when an alert is triggered.

Detection Query

//Cross-Workspace Query for detecting added Azure LH delegations
union  
workspace("Insert Log Analytics workspaceId").AzureActivity,
workspace("Insert Log Analytics workspaceId").AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue == "MICROSOFT.MANAGEDSERVICES"
| extend Action = tostring(parse_json(Authorization).action)
| extend ['PrincipalType'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).principalType)
| extend ['PrincipalId'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).principalId)
| extend ['Role'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).role)
| extend ['RoleAssignmentId'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).roleAssignmentId)
| extend ['RoleAssignmentScope'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).roleAssignmentScope)
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['ClientIpAddress'] = tostring(parse_json(tostring(parse_json(Properties).httpRequest)).clientIpAddress)
| where Action contains "write"
| project TimeGenerated, Action, ['PrincipalId'], ['RoleAssignmentId'], ['Scope'], Caller, ['CallerIpAddress']


//Azure LH delegation deleted
union  
workspace("Insert Log Analytics workspaceId").AzureActivity,
workspace("Insert Log Analytics workspaceId").AzureActivity
| where CategoryValue == 'Administrative'
| where ResourceProviderValue == "MICROSOFT.MANAGEDSERVICES"
| extend Action = tostring(parse_json(Authorization).action)
| extend ['PrincipalType'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).principalType)
| extend ['PrincipalId'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).principalId)
| extend ['Role'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).role)
| extend ['RoleAssignmentId'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).roleAssignmentId)
| extend ['RoleAssignmentScope'] = tostring(parse_json(tostring(parse_json(Authorization).evidence)).roleAssignmentScope)
| extend ['Scope'] = tostring(parse_json(Authorization).scope)
| extend ['ClientIpAddress'] = tostring(parse_json(tostring(parse_json(Properties).httpRequest)).clientIpAddress)
| where Action contains "delete"
| project TimeGenerated, Action, ['PrincipalId'], ['RoleAssignmentId'], ['Scope'], Caller, ['CallerIpAddress']

Option 2 – Microsoft Defender for Cloud Apps (MDA)

If you have been following my blog, you might have noticed that I’ve been a big fan of MDA for many years. MDA is an interesting solution in the Microsoft ecosystem that’s often overlooked. Taking Azure into account, it received very rich information from Azure Management API and in some cases provides a way to tackle security monitoring use cases that native log solutions (such as Azure Activity Log) don’t provide.

MDA events related to registration/unregistrations are visible in both MSSP and Client environments. Therefore, you can build the detection to either. Keep in mind that there are variations between events depending on where those are generated. However, as described below, it is not recommended to rely on visibility to the Client environment and I would use managing tenant MDA as the preferred option.  

Pros:

  • Detection rules based on MDA data can be created in MDA, M365 Defender, or in Sentinel
  • A very effective way to do the detection that can be used to create either Sentinel alerts or Azure Monitoring Alerts
  • Azure Lighthouse delegation additions or changes are visible (create/modify)

Cons:

  • Requires appropriate licenses
    • If the detection rule is running on the Client environment and you lose access, you are missing the events & alerts

Detection Query

Here you can find two queries that can be run on the Azure Log Analytics side if MDA raw data (CloudAppEvents) is being ingested into the workspace. The first query is for the Azure Lighthouse delegation registration and the second one is for un-registration. The query could be run also as a cross-workspace query.

//Azure LH tenant registration
CloudAppEvents
| where ApplicationId == 12260
| extend ResourceProvider = RawEventData.resourceProvider
| extend OperationName =  RawEventData.operationName
| extend Description = RawEventData.description
| extend Status = RawEventData.status
| extend subscriptionId = tostring(parse_json(tostring(RawEventData.description)).subscriptionId)
| extend subscriptionTenantId = tostring(parse_json(tostring(RawEventData.description)).subscriptionTenantId)
| extend delegationResourceId = tostring(parse_json(tostring(RawEventData.description)).delegationResourceId)
| extend Status = RawEventData.status
| where Status == 'Succeeded'
| where OperationName has "Microsoft.Resources/tenants/register/action"
| project TimeGenerated, ActionType, Status, Description, AccountDisplayName, IPAddress, CountryCode, ResourceProvider, OperationName, subscriptionId, subscriptionTenantId, delegationResourceId
//Azure LH tenant unregistration
CloudAppEvents
| where ApplicationId == 12260
| extend ResourceProvider = RawEventData.resourceProvider
| extend OperationName =  RawEventData.operationName
| extend Description = RawEventData.description
| extend subscriptionId = tostring(parse_json(tostring(RawEventData.description)).subscriptionId)
| extend subscriptionTenantId = tostring(parse_json(tostring(RawEventData.description)).subscriptionTenantId)
| extend delegationResourceId = tostring(parse_json(tostring(RawEventData.description)).delegationResourceId)
| extend Status = RawEventData.status
| where Status == 'Succeeded'
| where OperationName has "Microsoft.Resources/tenants/unregister/action"
| project Timestamp, ActionType, Status, Description, AccountDisplayName, IPAddress, CountryCode, ResourceProvider, OperationName, subscriptionId, subscriptionTenantId, delegationResourceId

Option 3 – Azure Management API

One way approach is to use to authenticate to the AAD tenant with Service Principal (SP) and query Azure Management API changes from Azure Directory level Log (where management API is pushing the events).

Based on the Microsoft documentation, to get the Activity Log events your app will need ‘Monitoring Reader’ permissions. These permissions can be set on the subscription level to receive necessary audit data from the Activity Log (it’s a tenant-level API where events are stored).

The article is found here but we couldn’t get it working as expected. We were able to retrieve the events, but not without granting the SP full Global Admin access, which obviously poses significant security risks. The API is from the year 2015 so it might be that this solution is not supported anymore.

Summary

Monitoring and detecting changes in management connections towards client environments is crucial for Managed Security Service Providers (MSSPs) to ensure effective security management. By actively tracking and analyzing these changes, MSSPs can identify potentially suspicious activities, or deviations from normal behavior, enabling them to promptly respond to maintain a robust security posture for their clients.

All detection queries are found in my GitHub repo.