Improve weak password test credential diagnostics and docs

This commit is contained in:
Tom Frost
2026-02-17 12:40:04 +01:00
parent 7874c0e65b
commit 7f1df7b102
2 changed files with 61 additions and 5 deletions

View File

@@ -46,7 +46,7 @@ Every run also emits a cleaned, DSInternals-friendly `khdb-clean.txt` beside the
When `-ForcePlainText` is specified the script automatically keeps a checkpoint (default: `<output>/khdb.checkpoint.json`) and resumes from it on the next run so massive inputs dont restart from scratch. Use `-CheckpointPath` to relocate that file or `-NoCheckpoint` to disable the behavior entirely. When `-ForcePlainText` is specified the script automatically keeps a checkpoint (default: `<output>/khdb.checkpoint.json`) and resumes from it on the next run so massive inputs dont restart from scratch. Use `-CheckpointPath` to relocate that file or `-NoCheckpoint` to disable the behavior entirely.
### Test Weak AD passwords ### Test Weak AD passwords
Run script Elysium.ps1 as an administrator and choose option 2 (Test Weak AD Passwords). Run script Elysium.ps1 as an administrator and choose option 2 (Test Weak AD Passwords).
The script will list domains in the same order as they appear in `ElysiumSettings.txt` and, after you pick one, prompt for the corresponding domain administrator password (the username is taken from the settings file). The script lists domains in the same order as they appear in `ElysiumSettings.txt`. After you pick one, it prompts for credentials and validates them against the selected domain controller before running the password-quality test.
The tool connects to the selected Domain Controller and compares accounts against KHDB (respecting the optional `CheckOnlyEnabledUsers` flag if configured). A timestamped text report is saved under `Reports`, and accounts with dictionary hits are also exported to a dedicated UPN-only text file to support follow-up automation. The tool connects to the selected Domain Controller and compares accounts against KHDB (respecting the optional `CheckOnlyEnabledUsers` flag if configured). A timestamped text report is saved under `Reports`, and accounts with dictionary hits are also exported to a dedicated UPN-only text file to support follow-up automation.
The KHDB file is consumed via binary search as a sorted hash list (plain text lines like `HASH:count`); ensure the file you place at `khdb.txt` keeps that ordering and omits stray blank lines. The KHDB file is consumed via binary search as a sorted hash list (plain text lines like `HASH:count`); ensure the file you place at `khdb.txt` keeps that ordering and omits stray blank lines.
@@ -58,6 +58,14 @@ The DSInternals cmdlets (`Get-ADReplAccount`/`Test-PasswordQuality`) pull replic
To delegate, enable Advanced Features in ADUC, right-click the domain, choose *Delegate Control…*, pick the service account, select *Create a custom task*, apply to *This object and all descendant objects*, and tick the three replication permissions above. Keep this account disabled and only activate it for scheduled tests. To delegate, enable Advanced Features in ADUC, right-click the domain, choose *Delegate Control…*, pick the service account, select *Create a custom task*, apply to *This object and all descendant objects*, and tick the three replication permissions above. Keep this account disabled and only activate it for scheduled tests.
#### Common errors
- `The server has rejected the client credentials.` or `Credentials ... were rejected`:
The supplied username/password is invalid for the selected domain controller, or the session is not running in the expected domain context. Re-run and provide valid domain credentials.
- `Get-ADReplAccount: Access is denied`:
Credentials are valid, but the account does not have the three replication permissions listed above.
- `Only FIPS certified cryptographic algorithms are enabled in .NET`:
This warning comes from DSInternals under FIPS-enforced environments. Hash-quality operations that rely on MD5 may be limited.
#### Optional usage beacon #### Optional usage beacon
If you want to know the script was executed without collecting telemetry, set a pre-signed URL (for example, an S3 `PUT` URL) in `UsageBeaconUrl` inside `ElysiumSettings.txt`. When present, the weak-password script issues a single request as soon as it loads the settings. Only the script name, its version, a UTC timestamp, and the optional `UsageBeaconInstanceId` value are sent, and network failures never block the run. Choose the HTTP verb via `UsageBeaconMethod` (`GET`, `POST`, or `PUT`) and adjust the timeout with `UsageBeaconTimeoutSeconds` if your storage endpoint needs more time. If you want to know the script was executed without collecting telemetry, set a pre-signed URL (for example, an S3 `PUT` URL) in `UsageBeaconUrl` inside `ElysiumSettings.txt`. When present, the weak-password script issues a single request as soon as it loads the settings. Only the script name, its version, a UTC timestamp, and the optional `UsageBeaconInstanceId` value are sent, and network failures never block the run. Choose the HTTP verb via `UsageBeaconMethod` (`GET`, `POST`, or `PUT`) and adjust the timeout with `UsageBeaconTimeoutSeconds` if your storage endpoint needs more time.

View File

@@ -402,12 +402,42 @@ function Get-UserUPN {
# (removed stray top-level loop; UPN enrichment happens during report generation below) # (removed stray top-level loop; UPN enrichment happens during report generation below)
function Get-ValidatedADCredential {
param (
[Parameter(Mandatory)][string]$DomainName,
[Parameter(Mandatory)][string]$Server,
[int]$MaxAttempts = 3
)
for ($attempt = 1; $attempt -le $MaxAttempts; $attempt++) {
$credential = Get-Credential -Message "Enter AD credentials with replication rights for $DomainName (attempt $attempt/$MaxAttempts)"
if ($null -eq $credential) {
throw "Credential prompt was cancelled."
}
try {
Get-ADDomain -Server $Server -Credential $credential -ErrorAction Stop | Out-Null
Write-Verbose ("Credential pre-check succeeded for '{0}' against '{1}'." -f $credential.UserName, $Server)
return $credential
} catch {
$message = $_.Exception.Message
if ($message -match 'rejected the client credentials|unknown user name|bad password|logon failure') {
Write-Warning ("Credentials were rejected for '{0}' (attempt {1}/{2})." -f $credential.UserName, $attempt, $MaxAttempts)
if ($attempt -lt $MaxAttempts) { continue }
throw "Credentials were rejected by domain controller '$Server' after $MaxAttempts attempts."
}
throw "Credential pre-check failed against '$Server': $message"
}
}
}
# Function to test for weak AD passwords # Function to test for weak AD passwords
function Test-WeakADPasswords { function Test-WeakADPasswords {
param ( param (
[hashtable]$DomainDetails, [hashtable]$DomainDetails,
[string]$FilePath, [string]$FilePath,
[bool]$CheckOnlyEnabledUsers = $false [bool]$CheckOnlyEnabledUsers = $false,
[System.Management.Automation.PSCredential]$Credential
) )
# User selects a domain # User selects a domain
@@ -423,8 +453,17 @@ function Test-WeakADPasswords {
$selectedDomain = $DomainDetails[$selection] $selectedDomain = $DomainDetails[$selection]
Write-Verbose "Selected domain: $($selectedDomain.Name)" Write-Verbose "Selected domain: $($selectedDomain.Name)"
# Prompt for DA credentials if ([string]::IsNullOrWhiteSpace($selectedDomain["DC"])) {
$credential = Get-Credential -Message "Enter AD credentials with replication rights for $($selectedDomain.Name)" Write-Error ("Domain '{0}' does not have a configured DC in ElysiumSettings.txt." -f $selectedDomain.Name)
return
}
if ($null -eq $Credential) {
$credential = Get-ValidatedADCredential -DomainName $selectedDomain.Name -Server $selectedDomain["DC"]
} else {
$credential = $Credential
Write-Verbose ("Using credential supplied by caller: {0}" -f $credential.UserName)
}
# Performing the test # Performing the test
Write-Verbose "Testing password quality for $($selectedDomain.Name)..." Write-Verbose "Testing password quality for $($selectedDomain.Name)..."
@@ -440,7 +479,16 @@ function Test-WeakADPasswords {
$testResults = $accounts | Test-PasswordQuality -WeakPasswordHashesSortedFile $FilePath $testResults = $accounts | Test-PasswordQuality -WeakPasswordHashesSortedFile $FilePath
Write-Verbose "Password quality test completed." Write-Verbose "Password quality test completed."
} catch { } catch {
Write-Error ("An error occurred while testing passwords: {0}" -f $_.Exception.Message) $message = $_.Exception.Message
if ($message -match 'Access is denied') {
Write-Error ("Access denied while reading replication data from '{0}' using '{1}'. Ensure this account has Replicating Directory Changes, Replicating Directory Changes All, and Replicating Directory Changes In Filtered Set on the domain." -f $selectedDomain["DC"], $credential.UserName)
return
}
if ($message -match 'rejected the client credentials|unknown user name|bad password|logon failure') {
Write-Error ("Credentials for '{0}' were rejected by '{1}'. Re-run and provide valid domain credentials." -f $credential.UserName, $selectedDomain["DC"])
return
}
Write-Error ("An error occurred while testing passwords: {0}" -f $message)
return return
} }