Unveiling Hidden Persistence: The "Directory Synchronization Accounts" Role
Kloudynet Technologies
Experts in Cloud Governance | Cybersecurity | Compliance | SOC | MDR | Microsoft's Advanced Security Solutions Partner
Summary
This article provides KQL queries to detect Stealthy Persistence achieved through assigning the "Directory Synchronization Accounts" role, which grants significant permissions and potential paths for privilege escalation.
What is "Directory Synchronization Accounts" role?
Before understanding what is the role of "Directory Synchronization Accounts" we have to first understand what is Microsoft Entra Connect(Formerly Azure AD Connect)?.
Microsoft Entra Connect (Azure AD Connect) is an on-premises tool designed to achieve hybrid identity goals by synchronizing on-premises Active Directory (AD) identities with Azure AD. This synchronization enables Single Sign-On (SSO) and federated identity services.
Microsoft Entra Connect uses three accounts to synchronize information from on-premises Windows Server Active Directory (Windows Server AD) to Microsoft Entra ID:
These accounts are set up as part of the Azure AD Connect installation. The "Microsoft Entra Connector Account(AAD Connector Account)" is particularly noteworthy as it is created in Microsoft Entra Id(Azure AD) for each AAD Connect Server. This account is identified within Entra ID with the display name "On-Premises Directory Synchronization Service Account." and with UPN structure “SYNC_<name of the on-prem server where Entra Connect runs>_<random id>@example.com”
As per Microsoft Documentation
These accounts(above shown Microsoft Entra Connect Accounts) are automatically assigned a specialized "Directory Synchronization Accounts" role, granting permissions solely for directory synchronization tasks.
"This is a privileged role. Do not use. This role is automatically assigned to the Microsoft Entra Connect and Microsoft Entra Cloud Sync services, and is not intended or supported for any other use."
What are privileged roles?
"Microsoft Entra ID has roles and permissions that are identified as privileged. These roles and permissions can be used to delegate management of directory resources to other users, modify credentials, authentication or authorization policies, or access restricted data. Privileged role assignments can lead to elevation of privilege if not used in a secure and intended manner."
Microsoft says this role is solely used for directory synchronization tasks. But it can also be misused for privilege escalation since it has very high level permissions such as .
Since this role is intended solely for specific purposes, Microsoft has hidden this rule from Entra ID (Azure AD), as demonstrated.
So, you cannot assign this role to anyone via the Azure portal. Additionally, you cannot view the list of users assigned to this role, making it particularly stealthy if a threat actor gains access to this role through a compromised account.
But this role can indeed be assigned to any user via Microsoft Graph PowerShell. This technique was identified by "Clément Notin" in his blog post, where he also reported the issue to Microsoft for resolution as shown.
He provides following powershell cmdlets in his blog for performing the role assignment. Read more from his blog
List Role Assignments
With just the "Domain.Read.All" permission, you can list which accounts have been assigned the "Directory Synchronization Accounts" role. For listing out the command is using the role templateId "d29b2b05-8046-44ba-8758-1e26182fcf32" (role template IDs are unique and constant in all environments for entra id roles)
Connect-MgGraph -Scopes "Domain.Read.All"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
Get-MgDirectoryRoleMember -DirectoryRoleId $dirSync.Id | Format-List *
Add role assignment
The below powershell cmdlets add Directory Synchronization Account role to hacker1 user. For role assignment the "RoleManagement.ReadWrite.Directory" permission is required . If your tenant has any application with this permission(RoleManagement.ReadWrite.Directory) it can be abused for privilege escalation and for assigning the directory synchronization role assignment as shown.
领英推荐
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
$hacker = Get-MgUser -UserId [email protected]
New-MgRoleManagementDirectoryRoleAssignment -RoleDefinitionId $dirSync.Id -PrincipalId $hacker.Id -DirectoryScopeId "/"
Detection
Even though its stealthy method it can be detected from AuditLogs and IdentityInfo table logs as shown.
Adding "Directory Synchronization Role" to a user can be detected by using below KQL query.
AuditLogs
| where TimeGenerated >ago(15m)
| where OperationName=="Add member to role"
| where Result=="success"
| mv-expand TargetResources
| extend RoleAssigned=iff(tostring(TargetResources.modifiedProperties[1].displayName)=="Role.DisplayName",tostring(TargetResources.modifiedProperties[1].newValue),'')
| extend RoleObjectID=iff(tostring(TargetResources.modifiedProperties[0].displayName)=="Role.ObjectID",tostring(TargetResources.modifiedProperties[0].newValue),'')//Specifies the ID of a directory role in Azure AD.
| extend RoleObjectName=iff(tostring(TargetResources.modifiedProperties[3].displayName)=="Role.WellKnownObjectName",tostring(TargetResources.modifiedProperties[3].newValue),'')
| extend ['User-Agent']=iff(AdditionalDetails[0].key=="User-Agent", AdditionalDetails[0].value,'')
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatedVia=tostring(InitiatedBy.user.displayName)
| extend ipAddress=tostring(InitiatedBy.user.ipAddress)
| extend TargetPrincipalName=tostring(TargetResources.userPrincipalName)
| where RoleAssigned=~'"Directory Synchronization Accounts"'
| project TimeGenerated,AADOperationType,OperationName, InitiatedUser, InitiatedVia,ipAddress, TargetPrincipalName, RoleAssigned, RoleObjectID, RoleObjectName, ['User-Agent']
The IdentityInfo table provides information on built-in role assignments through the "AssignedRoles" field property.
IdentityInfo
| where TimeGenerated > ago(1h)
| where AssignedRoles contains "Directory Synchronization Accounts"
KQL query for detecting the removal of role.
AuditLogs
| where TimeGenerated >ago(40m)
| where OperationName=="Remove member from role"
| where Result=="success"
| mv-expand TargetResources
| extend RoleAssigned=iff(tostring(TargetResources.modifiedProperties[1].displayName)=="Role.DisplayName",tostring(TargetResources.modifiedProperties[1].oldValue),'')
| extend RoleObjectID=iff(tostring(TargetResources.modifiedProperties[0].displayName)=="Role.ObjectID",tostring(TargetResources.modifiedProperties[0].oldValue),'')//Specifies the ID of a directory role in Azure AD.
| extend RoleObjectName=iff(tostring(TargetResources.modifiedProperties[3].displayName)=="Role.WellKnownObjectName",tostring(TargetResources.modifiedProperties[3].oldValue),'')
| extend ['User-Agent']=iff(AdditionalDetails[0].key=="User-Agent", AdditionalDetails[0].value,'')
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatedVia=tostring(InitiatedBy.user.displayName)
| extend ipAddress=tostring(InitiatedBy.user.ipAddress)
| extend TargetPrincipalName=tostring(TargetResources.userPrincipalName)
| where RoleAssigned=~'"Directory Synchronization Accounts"'
| project TimeGenerated,AADOperationType,OperationName, InitiatedUser, InitiatedVia,ipAddress, TargetPrincipalName, RoleAssigned, RoleObjectID, RoleObjectName, ['User-Agent']
During the role assignment operation you will see the following operations in graphactivity logs. The below KQL query for activities where a role assignment is performed via Microsoft Graph API (POST method) with a successful response status code (201 Created).
MicrosoftGraphActivityLogs
| where TimeGenerated >ago(1h)
| where RequestUri contains "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"
| where RequestMethod=="POST"
//optional
//| where Scopes contains "RoleManagement.ReadWrite.Directory"
| where ResponseStatusCode==201
Before attackers proceed with any role assignment, it is common for them to review existing users and their current role assignments. below query looks for recon activity for the "Directory Synchronization Accounts" role.
MicrosoftGraphActivityLogs
| where TimeGenerated >ago(1d)
| where RequestMethod=="GET"
| where ResponseStatusCode==200
| where RequestUri has_all ('directoryRoles?$filter=RoleTemplateId','27d29b2b05-8046-44ba-8758-1e26182fcf32')
// ex: https://graph.microsoft.com/v1.0/directoryRoles?$filter=RoleTemplateId%20eq%20%27d29b2b05-8046-44ba-8758-1e26182fcf32%27"
The usage of Microsoft Graph PowerShell can be identified through UserAgent values such as:
MicrosoftGraphActivityLogs
| where TimeGenerated >ago(1d)
| where UserAgent contains "Powershell"
| where ResponseStatusCode in (200,201,204)
To find Microsoft Graph PowerShell logins from the sign-in logs, you can use a below query:
SigninLogs
| where TimeGenerated >ago(1d)
| where AppDisplayName=="Microsoft Graph Command Line Tools"
| where ResultType==0
Remediation
"Clément Notin" provided some good remediation steps in his blog post. To safeguard against unauthorized access, you can implement a Conditional Access policy that blocks all users except the legitimate synchronization user with the Directory Synchronization Accounts role. Alternatively, you can restrict this role to specific IP addresses(used by legit on-premise Entra connect servers).
References: