ADTIER-001: Azure AD Connect Sync Account (MSOL_) Audit
- Platform
- Active Directory
- Category
- Tier-0 Hygiene & Hybrid Identity Surface
- Severity
- Critical
- Zero Trust pillar
- Identity (weight 3)
- Golden fixtures
- 3
- Branch coverage
- Observed: fixtures prove the verdicts they exercise
- Provenance
- baseline
What it checks
The check enumerates every account in the forest whose sAMAccountName matches MSOL_* and every account whose description references "Account created by Microsoft Azure Active Directory Connect" or whose servicePrincipalName contains the AAD Connect sync prefix. For each MSOL_ account it reads: - distinguishedName (to confirm the account is not sitting in CN=Users or another Tier-1 OU) - pwdLastSet (converted to days since last rotation) - userAccountControl (to confirm PASSWD_NOTREQD and DONT_EXPIRE_PASSWORD are not unexpectedly set, and to flag the account if it is disabled while sync is still running) - memberOf (sync accounts should not be a direct member of any privileged group, their power comes from the ACE) - lastLogonTimestamp (a stale MSOL_ account that no longer corresponds to a running sync server is a residual Tier-0 backdoor) - servicePrincipalName, msDS-AllowedToActOnBehalfOfOtherIdentity, and msDS-AllowedToDelegateTo (to detect Kerberos delegation abuse vectors stacked on top of the account) The check then reads the nTSecurityDescriptor on the domain head and walks the DACL for ACEs assigned to the MSOL_ account. It confirms the expected DS-Replication-Get-Changes (GUID 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2) and DS-Replication-Get-Changes-All (GUID 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2) ACEs are present, and flags any additional grants such as GenericAll, WriteDacl, or Replicating Directory Changes In Filtered Set that exceed the documented AAD Connect requirement. Finally, the check enumerates AAD Connect server objects (typically tagged via servicePrincipalName ADSync/* or via the registered SCP CN=62a0ff2e-97b9-4513-943f-0d221bd30080) and validates they are in a Tier-0 OU.
Why it matters
The MSOL_ account is a domain-wide DCSync principal that is almost always overlooked during Tier-0 modelling. It is created automatically, has a non-obvious name, lives outside the protected groups, and is rotated on a long cadence (the AAD Connect tooling rotates it every 30 days for Entra Connect Sync v2 builds, but earlier versions used a 10-year expiry and never rotated unless the operator ran Add-ADSyncADDSConnectorAccount). Any of the following turns a Tier-1 compromise into a domain takeover: - Reading the MSOL_ password from the AAD Connect configuration database (encrypted with DPAPI keyed to the AAD Connect service account, recoverable from the AAD Connect host by anyone with local admin via AADInternals Get-AADIntSyncCredentials). - Coercing the AAD Connect server (which is a member server in most deployments) and relaying its machine account, then pivoting to local admin and extracting the MSOL_ credential. - Pass-the-hash or pass-the-ticket against the AAD Connect host if the operator logs on interactively with a Domain Admin account. - Reading the AD Sync connector account credential via the legacy MIIS WMI provider on older AAD Connect builds. Once the MSOL_ credential is in hand, the attacker runs DCSync against any DC, dumps the krbtgt hash, and forges Golden Tickets. The attack leaves no group-membership trail, so detections that key on Domain Admins / Enterprise Admins additions miss it entirely. The AAD Connect host is also the on-premises endpoint that writes back into Entra ID, so compromise of the host (and therefore the cloud sync account, often called Sync_<servername>_<hex> or AAD_<hex>) can be chained into Entra ID Global Administrator via Seamless SSO key theft or token forgery against the AZUREADSSOACC$ computer account.
Attack path
1. Recon: the attacker enumerates accounts matching MSOL_* via LDAP (Get-ADUser -Filter {sAMAccountName -like "MSOL_*"}) or via BloodHound (SharpHound -CollectionMethod ACL surfaces the GetChanges and GetChangesAll edges from the MSOL_ account to the domain). The AAD Connect server is identified through the SCP at CN=62a0ff2e-97b9-4513-943f-0d221bd30080,CN=Microsoft,CN=Services,CN=Configuration,DC=domain,DC=tld or by the presence of the ADSync service. 2. Foothold on the AAD Connect host: the attacker gains local admin on the AAD Connect server, often by coercing the server (PetitPotam / DFSCoerce) and relaying to ADCS, or by RDP from a Tier-1 jump host that was never restricted from logging on to the AAD Connect host. 3. Credential extraction: with local admin, the attacker runs AADInternals Get-AADIntSyncCredentials, which queries the ADSync SQL LocalDB instance, decrypts the encrypted blob using DPAPI keyed to the ADSync service account, and returns both the on-prem MSOL_ password and the cloud Sync_*/AAD_* credential in plaintext. Alternative paths use mimikatz lsadump::dcsync once an interactive Domain Admin logon has cached credentials on the host, or direct SQL queries against the mms_management_agent table. 4. On-prem DCSync: armed with the MSOL_ password the attacker authenticates to any domain controller and runs secretsdump.py domain/MSOL_xxxxxxxxxxxx@dc01 or mimikatz lsadump::dcsync /domain:corp.tld /user:krbtgt. The krbtgt hash is now compromised and a Golden Ticket can impersonate any principal. 5. Cloud pivot: the same extraction yields the cloud sync account, which holds the Directory Synchronization Accounts role in Entra ID. AADInternals Set-AADIntUserPassword can reset any cloud user password (including Global Administrators in tenants where the synced principals match), and Open-AADIntOffice365Portal logs in as the cloud sync identity to pivot further. 6. Persistence: the attacker may add a rogue ACE on the domain object granting Replicating Directory Changes to a second controlled principal, leave the MSOL_ password intact (so no alert fires on rotation), and configure a scheduled task on the AAD Connect host to re-extract the credential after any future rotation.
How Guerrilla assesses it
Guerrilla queries LDAP for (|(sAMAccountName=MSOL_*)(description=*Microsoft Azure Active Directory Connect*)) with properties distinguishedName, sAMAccountName, pwdLastSet, userAccountControl, memberOf, lastLogonTimestamp, servicePrincipalName, msDS-AllowedToActOnBehalfOfOtherIdentity, and msDS-AllowedToDelegateTo. For each account returned it computes the password age in days from pwdLastSet, normalizes the parent container, and resolves the userAccountControl flags. It then binds the domain head (Get-ADObject -Identity (Get-ADDomain).DistinguishedName -Properties nTSecurityDescriptor) and walks the DACL, filtering ACEs by IdentityReference matching each MSOL_ account SID. ACEs are classified by ObjectType GUID into the expected GetChanges and GetChangesAll pair or into unexpected grants. The check also enumerates AAD Connect SCPs under CN=Microsoft,CN=Services,CN=Configuration to confirm the discovered MSOL_ accounts correspond to a registered sync endpoint and that the endpoint server object is in a Tier-0 OU. A finding is emitted if any of the following are true: the MSOL_ account is in CN=Users or a non Tier-0 OU, pwdLastSet exceeds 180 days, the account holds rights beyond the documented GetChanges / GetChangesAll pair, the account has a non-empty servicePrincipalName or a delegation attribute set, or the corresponding AAD Connect server is missing from a Tier-0 OU. Results are surfaced with the account DN, password age, ACE inventory, AAD Connect server DN, and a remediation reference.
Recommended value
All MSOL_ accounts are inventoried, password rotated within the last 180 days, placed in a Tier-0 OU with restricted logon rights, and the AAD Connect server itself is hardened as a Tier-0 system.
Remediation
Locate the MSOL_ account: Get-ADUser -Filter {sAMAccountName -like 'MSOL_*'}. Confirm it has DCSync rights: dsacls 'DC=domain,DC=com' | findstr MSOL_. Move it to a Tier-0 admin OU. Rotate the password using the AAD Connect tooling (do NOT reset via standard tooling — use Add-ADSyncADDSConnectorAccount in the ADSync PowerShell module on the AAD Connect server). Apply a logon-restriction GPO so the account can only log on locally to the AAD Connect server itself. Treat the AAD Connect host as Tier-0 — restrict who can RDP/admin it.
Fixture-proven verdicts
Every verdict below is proven by a golden fixture in the module's gating test suite. This table derives from the last green run; it cannot be edited by hand.
| Scenario | Expected verdict |
|---|---|
| clean | PASS |
| known-bad | WARN |
| throttled | Not Assessed |
Framework mappings
- NIST SP 800-53
- AC-6, IA-5
- CIS AD Benchmark
- 8.1.1
- MITRE ATT&CK
- T1003.006, T1078.004