# This module contains functions to extract and update AADConnect sync credentials
# Oct 29th 2019
function Check-Server
{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$true)]
[bool]$force
)
process
{
# Always export as ADSync user
$AsADSync = $true
# Check that we are on AADConnect server and that the service is running
if($force -ne $true -and (($adSyncService = Get-Service ADSync -ErrorAction SilentlyContinue) -eq $null -or $adSyncService.Status -ne "Running"))
{
Write-Error "This command needs to be run on a computer with ADSync running!"
return $false
}
# Add the encryption reference (should always be there)
$ADSyncLocation = (Get-ItemProperty -Path "HKLM:SOFTWARE\Microsoft\AD Sync").Location
Add-Type -path "$ADSyncLocation\Bin\mcrypt.dll"
$ADSyncUser=""
$CurrentUser = "{0}\{1}" -f $env:USERDOMAIN,$env:USERNAME
# Check the version number: since 1.4.xx.xx uses DPAPI instead of registry to store the keyset
try
{
$serviceWMI = Get-WmiObject Win32_Service -Filter "Name='ADSync'" -ErrorAction SilentlyContinue
$ADSyncUser= $serviceWMI.StartName
$ver= ($serviceWMI.PathName.Split('"')[1] | Get-Item).VersionInfo.FileVersion
$ver2=$ver.split('.')
if($force -ne $true -and $ver2[0] -eq 1 -and $ver2[1] -ge 4 -and !$AsADSync)
{
Write-Warning "ADSync passwords can be read or modified as local administrator only for ADSync version 1.3.xx.xx!"
Write-Warning "The current version is $ver and access to passwords requires running as ADSync ($ADSyncUser)."
Write-Warning "Use the -AsADSync $true parameter to try again!"
return $false
}
}
catch
{
Write-Verbose "Could not get WMI info, probably already running as ADSync so skipping the ""elevation"""
$AsADSync = $false
}
# Elevate the current thread by copying the token from ADSync service
if($AsADSync)
{
# First we need to get connection once to the DB to get token.
# If done after "elevating" to ADSync, all SQL connections to configuration database will fail.
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList (Get-AADConfigDbConnection)
$SQLclient.Open()
$SQLclient.Close()
try
{
# Copy the tokens from lsass and miiserver (ADSync) processes
Write-Verbose "Trying to ""elevate"" by copying token from lsass and then miiserver (ADSync) processes"
$elevation = [AADInternals.Native]::copyLsassToken() -and [AADInternals.Native]::copyADSyncToken()
}
catch
{
$elevation = $false
}
if($elevation)
{
Write-Verbose """Elevation"" to ADSync succeeded!"
#Write-Warning "Running as ADSync ($ADSyncUser). You MUST restart PowerShell to restore $CurrentUser rights."
}
else
{
Write-Error "Could not change to $ADSyncUser. MUST be run as administrator!"
}
}
}
}
# May 15th 2019
function Get-SyncCredentials
{
<#
.SYNOPSIS
Gets Azure AD Connect synchronization credentials
.Description
Extracts Azure Active Directory Connect crecentials from WID configuration database. MUST be run on AADConnect server
as local administrator
.Example
Get-AADIntSyncCredentials
Name Value
---- -----
AADUser Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com
AADUserPassword $.1%(lxZ&/kNZz[r
ADDomain1 company.com
ADUser1 MSOL_4bc4a34e95fa
ADUserPassword1 Q9@p(poz{#:kF_G)(s/Iy@8c*9(t;...
ADDomain2 business.net
ADUser2 MSOL_4bc4a34e95fa
ADUserPassword2 cE/Pj+4/MR6hW)2L_4P=H^hiq)pZhMb...
.Example
PS C:\>$synccredentials = Get-AADIntSyncCredentials -AsCredentials
PS C:\>Get-AADIntAccessTokenForAADGraph -Credentials $synccredentials[0] -SaveToCache
Tenant User Resource Client
------ ---- -------- ------
a5427106-ed71-4185-9481-221e2ebdfc6c Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com https://graph.windows.net 1b730954-1685-4b74...
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory=$false)]
[bool]$AsBackgroundProcess=$true,
[Parameter(Mandatory=$false)]
[switch]$AsCredentials,
[Parameter(Mandatory=$false)]
[switch]$force
)
Process
{
# Check whether we are running in elevated session
Test-LocalAdministrator -Throw | Out-Null
# If started as a background process, start the background job script
if($AsBackgroundProcess)
{
# Check that we are on AADConnect server and that the service is running
if($force -ne $true -and (($adSyncService = Get-Service ADSync -ErrorAction SilentlyContinue) -eq $null -or $adSyncService.Status -ne "Running"))
{
Write-Error "This command needs to be run on a computer with ADSync running!"
return $false
}
Write-Verbose "Starting as a background process."
Try
{
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "powershell.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$pinfo.WorkingDirectory = $PSScriptRoot
$pinfo.Arguments = "-File AADSyncSettings_job.ps1"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
# Get the return value and convert from JSON string
$response = $p.StandardOutput.ReadToEnd()
Write-Verbose "Background process response: $response"
$retVal = $response | ConvertFrom-Json
$p.WaitForExit()
}
Catch
{
throw "Could not export credentials using background process."
}
}
else
{
# Do the checks
if((Check-Server -force $force) -eq $false)
{
return
}
# Read the encrypt/decrypt key settings
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList (Get-AADConfigDbConnection)
$SQLclient.Open()
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$SQLreader = $SQLcmd.ExecuteReader()
$SQLreader.Read() | Out-Null
$key_id = $SQLreader.GetInt32(0)
$instance_id = $SQLreader.GetGuid(1)
$entropy = $SQLreader.GetGuid(2)
$SQLreader.Close()
# Read the AD configuration data
$ADConfigs=@()
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE ma_type = 'AD'"
$SQLreader = $SQLcmd.ExecuteReader()
while($SQLreader.Read())
{
$ADConfig = $SQLreader.GetString(0)
$ADCryptedConfig = $SQLreader.GetString(1)
$ADConfigs += New-Object -TypeName psobject -Property @{"ADConfig" = $ADConfig; "ADCryptedConfig" = $ADCryptedConfig}
}
$SQLreader.Close()
# Read the AAD configuration data
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT private_configuration_xml, encrypted_configuration FROM mms_management_agent WHERE subtype = 'Windows Azure Active Directory (Microsoft)'"
$SQLreader = $SQLcmd.ExecuteReader()
$SQLreader.Read() | Out-Null
$AADConfig = $SQLreader.GetString(0)
$AADCryptedConfig = $SQLreader.GetString(1)
$SQLreader.Close()
$SQLclient.Close()
# Extract the data
$attributes=[ordered]@{}
$attributes["AADUser"]=([xml]$AADConfig).MAConfig.'parameter-values'.parameter[0].'#text'
$attributes["AADUserPassword"]=""
try
{
# Decrypt config data
$KeyMgr = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$KeyMgr.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$KeyMgr.GetActiveCredentialKey([ref]$key)
$key2 = $null
$KeyMgr.GetKey(1, [ref]$key2)
# Extract the encrypted data
$n=1
foreach($ADConfig in $ADConfigs)
{
$ADDecryptedConfig = $null
$key2.DecryptBase64ToString($ADConfig.ADCryptedConfig, [ref]$ADDecryptedConfig)
$attributes["ADDomain$n" ]=([xml]$ADConfig.ADConfig).'adma-configuration'.'forest-login-domain'
$attributes["ADUser$n" ]=([xml]$ADConfig.ADConfig).'adma-configuration'.'forest-login-user'
$attributes["ADUserPassword$n"]=([xml]$ADDecryptedConfig).'encrypted-attributes'.attribute.'#text'
$n++
}
$AADDecryptedConfig = $null
$key2.DecryptBase64ToString($AADCryptedConfig, [ref]$AADDecryptedConfig)
$attributes["AADUserPassword"]=([xml]$AADDecryptedConfig).'encrypted-attributes'.attribute | Where name -eq "Password" | Select -ExpandProperty "#text"
$retVal = [PSCustomObject]$attributes
}
catch
{
Write-Error "Could not load key set!"
}
}
# Create credentials objects if requested
if($AsCredentials)
{
$credentials = @()
# There is only one AAD credentials
$credentials += New-Object System.Management.Automation.PSCredential($retVal.AADUser, (ConvertTo-SecureString $retVal.AADUserPassword -AsPlainText -Force))
# Loop through the on-prem AD credentials. Shouldn't be more than 100 :)
for($n = 1 ; $n -lt 100 ; $n++)
{
if(![string]::IsNullOrEmpty($retVal."ADUser$n"))
{
$userName = "$($retVal."ADDomain$n")\$($retVal."ADUser$n")"
$credentials += New-Object System.Management.Automation.PSCredential($userName, (ConvertTo-SecureString $retVal."ADUserPassword$n" -AsPlainText -Force))
}
else
{
# No more on-prem AD credentials
break
}
}
return @($credentials)
}
else
{
return $retVal
}
}
}
# May 16th 2019
function Update-SyncCredentials
{
<#
.SYNOPSIS
Updates Azure AD Connect synchronization credentials
.Description
Updates Azure Active Directory Connect user's password to Azure AD and WID configuration database. MUST be run on AADConnect server
as local administrator with Global Admin credentials to Azure AD
.Example
Update-AADIntSyncCredentials
Password successfully updated to Azure AD and configuration database!
Remember to restart the sync service: Restart-Service ADSync
Name Value
---- -----
ADDomain company.com
ADUser MSOL_4bc4a34e95fa
ADUserPassword Q9@p(poz{#:kF_G)(s/Iy@8c*9(t;...
AADUser Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com
AADUserPassword $.1%(lxZ&/kNZz[r
.Example
Update-AADIntSyncCredentials -RestartADSyncService
Password successfully updated to Azure AD and configuration database!
Name Value
---- -----
ADDomain company.com
ADUser MSOL_4bc4a34e95fa
ADUserPassword Q9@p(poz{#:kF_G)(s/Iy@8c*9(t;...
AADUser Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com
AADUserPassword $.1%(lxZ&/kNZz[r
WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to stop...
WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to start...
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory=$False)]
[String]$AccessToken,
[Switch]$RestartADSyncService,
[Parameter(Mandatory=$false)]
[switch]$force
)
Process
{
# Do the checks
if((Check-Server -force $force) -eq $false)
{
return
}
# Get from cache if not provided
$AccessToken = Get-AccessTokenFromCache -AccessToken $AccessToken -ClientID "1b730954-1685-4b74-9bfd-dac224a7b894" -Resource "https://graph.windows.net"
if([String]::IsNullOrEmpty($AccessToken))
{
Write-Error "No AccessToken provided!"
return
}
# Admin user
$AdminUser = (Read-Accesstoken -AccessToken $at).upn
# Get the current configuration
$SyncCreds = Get-SyncCredentials -force
$SyncUser = ($SyncCreds.AADUser.Split("@")[0])
Write-Verbose "Updating password for $SyncUser as $AdminUser"
# Reset the account password in AzureAD
$NewPassword = (Reset-ServiceAccount -AccessToken $AccessToken -ServiceAccount $SyncUser).Password
# Escaping password for xml
$NewPassword = [System.Security.SecurityElement]::Escape($NewPassword)
if([String]::IsNullOrEmpty($NewPassword))
{
Write-Error "Password for user $SyncCreds could not be reset to Azure AD"
return
}
# Create a new config
$ADDecryptedConfig=@"
$NewPassword
"@
# Read the encrypt/decrypt key settings
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList (Get-AADConfigDbConnection)
$SQLclient.Open()
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$SQLreader = $SQLcmd.ExecuteReader()
$SQLreader.Read() | Out-Null
$key_id = $SQLreader.GetInt32(0)
$instance_id = $SQLreader.GetGuid(1)
$entropy = $SQLreader.GetGuid(2)
$SQLreader.Close()
# Load keys
$KeyMgr = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$KeyMgr.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$KeyMgr.GetActiveCredentialKey([ref]$key)
$key2 = $null
$KeyMgr.GetKey(1, [ref]$key2)
# Encrypt
$AADCryptedConfig = $null
$key2.EncryptStringToBase64($ADDecryptedConfig,[ref]$AADCryptedConfig)
# Write the updated AAD password
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "UPDATE mms_management_agent SET encrypted_configuration=@pwd WHERE subtype = 'Windows Azure Active Directory (Microsoft)'"
$SQLcmd.Parameters.AddWithValue("@pwd",$AADCryptedConfig) | Out-Null
$UpdatedRows = $SQLcmd.ExecuteNonQuery()
$SQLclient.Close()
if($UpdatedRows -ne 1)
{
Write-Error "Updated $UpdatedRows while should update 1. Could be error"
return
}
Write-Host "Password successfully updated to Azure AD and configuration database!"
# Return
Get-SyncCredentials -force
# Restart the ADSync service if requested
if($RestartADSyncService)
{
Restart-Service ADSync
}
else
{
Write-Host "Remember to restart the sync service: Restart-Service ADSync" -ForegroundColor Yellow
}
}
}
# May 17th 2019
function Set-ADSyncAccountPassword
{
<#
.SYNOPSIS
Sets the password of ADSync service account
.Description
Sets the password of ADSync service account to AD and WID configuration database. MUST be run on AADConnect server
as domain administrator.
.Example
Set-AADIntADSyncAccountPassword -NewPassword 'Pa$$w0rd'
Password successfully updated to AD and configuration database!
Remember to restart the sync service: Restart-Service ADSync
Name Value
---- -----
ADDomain company.com
ADUser MSOL_4bc4a34e95fa
ADUserPassword Pa$$w0rd
AADUser Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com
AADUserPassword $.1%(lxZ&/kNZz[r
.Example
Set-AADIntADSyncAccountPassword -NewPassword 'Pa$$w0rd' -RestartADSyncService
Password successfully updated to AD and configuration database!
Name Value
---- -----
ADDomain company.com
ADUser MSOL_4bc4a34e95fa
ADUserPassword Pa$$w0rd
AADUser Sync_SRV01_4bc4a34e95fa@company.onmicrosoft.com
AADUserPassword $.1%(lxZ&/kNZz[r
WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to stop...
WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to start...
#>
[cmdletbinding()]
Param(
[Parameter(Mandatory=$True)]
[String]$NewPassword,
[Switch]$RestartADSyncService,
[Parameter(Mandatory=$false)]
[switch]$force
)
Process
{
# Do the checks
if((Check-Server -force $force) -eq $false)
{
return
}
# Add the encryption dll reference
Add-Type -path "$(Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\AD Sync" -Name "Location")\Bin\mcrypt.dll"
# Get the current configuration
$SyncCreds = Get-SyncCredentials -force
$SyncUser = $SyncCreds.ADUser
Write-Verbose "Updating password for $SyncUser"
# Reset the account password in AD
try
{
Set-ADAccountPassword -Identity $SyncUser -Reset -NewPassword (ConvertTo-SecureString -AsPlainText $NewPassword -Force)
}
catch
{
# There might be complexity etc. requirements
throw $_
return
}
# Escaping password for xml
$NewPassword = [System.Security.SecurityElement]::Escape($NewPassword)
# Create a new config
$ADDecryptedConfig=@"
$NewPassword
"@
# Read the encrypt/decrypt key settings
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList (Get-AADConfigDbConnection)
$SQLclient.Open()
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$SQLreader = $SQLcmd.ExecuteReader()
$SQLreader.Read() | Out-Null
$key_id = $SQLreader.GetInt32(0)
$instance_id = $SQLreader.GetGuid(1)
$entropy = $SQLreader.GetGuid(2)
$SQLreader.Close()
# Load keys
$KeyMgr = New-Object -TypeName Microsoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManager
$KeyMgr.LoadKeySet($entropy, $instance_id, $key_id)
$key = $null
$KeyMgr.GetActiveCredentialKey([ref]$key)
$key2 = $null
$KeyMgr.GetKey(1, [ref]$key2)
# Encrypt
$ADCryptedConfig = $null
$key2.EncryptStringToBase64($ADDecryptedConfig,[ref]$ADCryptedConfig)
# Write the updated AA password
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "UPDATE mms_management_agent SET encrypted_configuration=@pwd WHERE ma_type = 'AD'"
$SQLcmd.Parameters.AddWithValue("@pwd",$ADCryptedConfig) | Out-Null
$UpdatedRows = $SQLcmd.ExecuteNonQuery()
$SQLclient.Close()
if($UpdatedRows -ne 1)
{
Write-Error "Updated $UpdatedRows while should update 1. Could be error"
return
}
Write-Host "Password successfully updated to AD and configuration database!"
# Return
Get-SyncCredentials -force
# Restart the ADSync service if requested
if($RestartADSyncService)
{
Restart-Service ADSync
}
else
{
Write-Host "Remember to restart the sync service: Restart-Service ADSync" -ForegroundColor Yellow
}
}
}
# Decrypts AD and AAD passwords with the given key and IV
# May 3rd 2020
function Get-DecryptedConfigPassword
{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$true)]
[byte[]]$Data,
[Parameter(Mandatory=$true)]
[byte[]]$Key,
[Parameter(Mandatory=$true)]
[guid]$InitialVector
)
Process
{
# Create the AES decryptor
$aes=New-Object -TypeName System.Security.Cryptography.AesCryptoServiceProvider
$aes.Mode = "CBC"
$aes.Key = $Key
$aes.IV = $iv.ToByteArray()
$dc=$aes.CreateDecryptor()
# Decrypt the data
$decData = $dc.TransformFinalBlock($Data,0,$Data.Length)
# Convert to xml and get the password
[xml]$decDataXml = ([text.encoding]::Unicode.GetString($decData)).trimEnd(@(0x00,0x0a,0x0d))
$decPassword = $decDataXml.'encrypted-attributes'.attribute.'#text'
Write-Verbose "DecryptedConfigPassword: $($decDataXml.OuterXml)"
# Return
return $decPassword
}
}
# Encrypts AD or AAD password with the given key and IV
# May 3rd 2020
function New-DecryptedConfigPassword
{
[cmdletbinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$Password,
[Parameter(Mandatory=$true)]
[byte[]]$Key,
[Parameter(Mandatory=$true)]
[guid]$InitialVector
)
Process
{
# Escaping password for xml
$NewPassword = [System.Security.SecurityElement]::Escape($Password)
# Create the AES encryptor
$aes=New-Object -TypeName System.Security.Cryptography.AesCryptoServiceProvider
$aes.Mode = "CBC"
$aes.Key = $Key
$aes.IV = $iv.ToByteArray()
$de=$aes.CreateEncryptor()
# Encrypt the data
$data = "$NewPassword"
$binData = [text.encoding]::Unicode.GetBytes($data)
$decData = $de.TransformFinalBlock($binData,0,$binData.Length)
Write-Verbose "DecryptedConfigPassword: $data"
# Return
return $decData
}
}
# Retrieves ADSync encryption key used to encrypt and decrypt configuration data
# May 3rd 2020
function Get-SyncEncryptionKey
{
<#
.SYNOPSIS
Gets ADSync encryption key using the given entropy and instance id
.DESCRIPTION
Gets the ADSync encryption key used to encrypt and decrypt passwords for service users of Azure AD and local AD
.Example
Get-AADIntSyncEncryptionKey -Entropy a1c80460-6fe9-4c6f-bf31-d7a34c878dca -InstanceId 299b1d83-9dc6-479a-92f1-2357fc5abfed
Id Guid CryptAlg Key
-- ---- -------- ---
100000 299b1d83-9dc6-479a-92f1-2357fc5abfed 26128 {4, 220, 54, 13...}
.Example
$key_info = Get-AADIntSyncEncryptionKeyInfo
PS C:\>Get-AADIntSyncEncryptionKey -Entropy $key_info.Entropy -InstanceId $key_info.InstanceId
Id Guid CryptAlg Key
-- ---- -------- ---
100000 299b1d83-9dc6-479a-92f1-2357fc5abfed 26128 {4, 220, 54, 13...}
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[guid]$Entropy,
[Parameter(Mandatory=$true)]
[guid]$InstanceId
)
# Define the return variable
$retVal = $null
# Fetch the full name of the ADSync user. Should be in the format DOMAIN\AAD_xxxxxxxxxxxx
$FullName=(Get-WmiObject Win32_Service -Filter "Name='ADSync'").StartName
$userName = $FullName.split("\")[1]
# Get user's SID
$userSID=(Get-WmiObject win32_useraccount -Filter "Name='$userName'").SID
# Get the stored password for the ADSync service -> this is the password of DOMAIN\AAD_xxxxxxxxxxxx user!
$LSAUserName = "_SC_ADSync"
$LSASecret=Get-LSASecrets -Users "_SC_ADSync"
$password=$LSASecret.PasswordTxt
Write-Verbose "UserName: $FullName"
Write-Verbose "SID: $userSID"
Write-Verbose "Password: $password`n`n"
# As we now know the password of the user, we can get user masterkeys without system masterkey
# Get user's masterkeys and decode them with username and password
#$masterKeys=Get-UserMasterkeys -UserName $userName -SID $userSID -Password $password
# Get the system key
$systemKey = Get-LSABackupKeys | Where-Object name -eq "RSA"
# Get the system masterkeys
$masterKeys = Get-SystemMasterkeys -SystemKey $systemKey.Key
# Get the user's masterkeys
$usrMasterKeys = Get-UserMasterkeys -UserName $userName -SystemKey $systemKey.Key -SID $userSID
# Merge the keys
foreach($key in $usrMasterKeys.Keys)
{
$masterKeys[$key]=$usrMasterKeys[$key]
}
# Get user's credentials with the masterkeys
$credentials = Get-LocalUserCredentials -UserName $userName -MasterKeys $masterKeys
# Try to find the correct credential entry
foreach($cred in $credentials)
{
$target = $cred.Target
# Check the target, we are looking for:
# LegacyGeneric:target=Microsoft_AzureADConnect_KeySet_{00000000-0000-0000-0000-0000000000}_100000
if($target.toLower().Contains(([guid]$instanceid).ToString()))
{
$keySetId = [int]$target.Split("_")[4]
# The keyset is actually a DPAPIBlob, so decrypt it using a native DPAPI method in LOCAL MACHINE context
$keySet = [AADInternals.Native]::getDecryptedData($cred.Secret,$entropy.toByteArray())
Write-Verbose "KeySet ($keySetId): $(Convert-ByteArrayToHex -Bytes $keySet)"
# Parse the keyset
$key = Parse-KeySetBlob -Data $keySet
# Check whether the id and guid matches
if($key.Id -eq $keySetId -and $key.Guid -eq $instanceId)
{
$retVal = $key
}
}
}
return $retVal
}
# Parses the MMSK key set blob
# May 3rd 2020
function Parse-KeySetBlob
{
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[byte[]]$Data
)
Process
{
# Parse the KeySet
$p=4 # Skip the MMSK string at the beginning
$version = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$id = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$guid = [guid][byte[]]$Data[$p..$($p+15)]; $p+=16
$unk0 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk1 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk2 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk3 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk4 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$keyBlockSize = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$secondKeySize = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk7 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk8 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk9 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk10 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk11 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$unk12 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$enAlg = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$keyLength = [System.BitConverter]::ToInt32($Data,$p);$p+=4
$key = $Data[$p..$($p+$keyLength-1)]; $p+=$keyLength
#$unk15 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
#$enAlg2 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
#$keyLength2 = [System.BitConverter]::ToInt32($Data,$p);$p+=4
#$key2 = $Data[$p..$($p+$keyLength2-1)]; $p+=$keyLength2
Write-Verbose "*** KEYSET ***"
Write-Verbose "Id: $id"
Write-Verbose "Guid: $guid"
Write-Verbose "CryptAlg: $enAlg $($ALGS[$enAlg])"
Write-Verbose "Key: $(Convert-ByteArrayToHex -Bytes $key)`n`n"
$attributes=[ordered]@{
"Id" = $id
"Guid" = $guid
"CryptAlg" = $enAlg
"Key" = $key
}
return New-Object PSObject -Property $attributes
}
}
# Gets entropy and instanceid from the local configuration database
# May 6th 2020
function Get-SyncEncryptionKeyInfo
{
<#
.SYNOPSIS
Gets ADSync encryption key info from the local configuration database
.DESCRIPTION
Gets ADSync encryption key info from the local configuration database
.Example
Get-AADIntSyncEncryptionKeyInfo
Name Value
---- -----
InstanceId 299b1d83-9dc6-479a-92f1-2357fc5abfed
Entropy a1c80460-6fe9-4c6f-bf31-d7a34c878dca
#>
[CmdletBinding()]
param()
# Read the encrypt/decrypt key settings
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList (Get-AADConfigDbConnection)
$SQLclient.Open()
$SQLcmd = $SQLclient.CreateCommand()
$SQLcmd.CommandText = "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
$SQLreader = $SQLcmd.ExecuteReader()
$SQLreader.Read() | Out-Null
$key_id = $SQLreader.GetInt32(0)
$instance_id = $SQLreader.GetGuid(1)
$entropy = $SQLreader.GetGuid(2)
$SQLreader.Close()
$SQLClient.Close()
return New-Object PSObject @{Entropy = $entropy; InstanceId = $instance_id}
}
# Gets the db connection string from the registry
# May 11th
function Get-AADConfigDbConnection
{
[cmdletbinding()]
Param()
Begin
{
# Create the connection string for the configuration database
$parametersPath = "HKLM:\SYSTEM\CurrentControlSet\Services\ADSync\Parameters"
$dBServer = (Get-ItemProperty -Path $parametersPath).Server
$dBName = (Get-ItemProperty -Path $parametersPath).DBName
$dBInstance = (Get-ItemProperty -Path $parametersPath).SQLInstance
$connectionString = "Data Source=$dbServer\$dBInstance;Initial Catalog=$dBName"
# If not using local WID, use ADSync account credentials
if($dBServer -ne "(localdb)")
{
$connectionString += ";Integrated Security=true"
}
}
Process
{
Write-Verbose "ConnectionString=$connectionString"
return $connectionString
}
}