Validate-Manifest hardcoded shardSize -ne 2 as an error, but
Prepare-KHDBStorage.ps1's -ShardSize parameter validly accepts 1-8.
Merge-ShardsToFile doesn't use shardSize for anything structural - it
reads shard files purely by name/content - so the check added no
safety, only rejected manifests produced with any supported shard
size other than 2. Widened the check to the same 1-8 range instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The menu loop had try/finally with no catch. Update-KHDB.ps1,
Test-WeakADPasswords.ps1, and Extract-NTHashes.ps1 all run under
$ErrorActionPreference = 'Stop' and re-throw on failure, so any
runtime error (bad credentials, unreachable DC, network failure)
propagated uncaught through the parent and killed the whole
orchestrator instead of returning to the menu. Wrapped the switch in
try/catch so a failed option reports the error and redisplays the
menu.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Get-ADUser -Identity accepts a DN, GUID, SID, or sAMAccountName - not
a UPN. The SID/tokenGroups resolution stripped only a 'DOMAIN\'
prefix, so a UPN-formatted credential (user@domain.tld) had no
backslash to strip, -Identity threw on the full UPN, and the
exception was swallowed by the surrounding catch (Write-Warning +
return), silently skipping the entire DCSync ACL pre-check.
Now branches on the credential format: DOMAIN\user strips the prefix
as before, user@domain.tld resolves via
-Filter "UserPrincipalName -eq '...'", and a bare sAMAccountName is
used as-is. The follow-up tokenGroups lookup now uses the resolved
DistinguishedName instead of re-deriving the username format.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Elysium.ps1 used Read-Host without -AsSecureString (echoed the AES
passphrase to the console) and persisted it as plaintext in
HKCU\Environment via SetEnvironmentVariable, readable by anyone with
console visibility or local registry access. Switched to
Read-Host -AsSecureString and store ConvertFrom-SecureString's DPAPI
output (decryptable only by the same user on the same machine)
instead of the raw value.
Extract-NTHashes.ps1, the only other consumer, now decrypts that
DPAPI-protected string back to a plain string via
ConvertTo-SecureString + NetworkCredential right before handing it to
Protect-FileWithAES. A stale plaintext value from a prior version is
detected and the operator is prompted to re-enter it.
DPAPI protection is Windows-only, consistent with the rest of this
Windows/AD-only tool; not runnable end-to-end on this (non-Windows)
dev machine, verified by AST parse only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Live NTLM hashes are written unencrypted to a temp file before AES
protection is applied. Cleanup only runs in a finally block, so a
hard kill/crash between write and cleanup could leave plaintext
hashes on disk under a directory that inherits whatever broad ACL its
parent Temp folder has (worst case: C:\Windows\Temp when run as
SYSTEM). Strip inherited ACEs and grant only the current user on the
temp directory right after creating it, narrowing exposure for that
window.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two bugs in Uninstall-Elysium:
- $ElysiumPath came from Get-Location (the caller's current working
directory) instead of $PSScriptRoot. Running the script from any
CWD other than the install folder recursively force-deleted the
wrong directory.
- The self-delete workaround was also broken: Remove-Item -Exclude
matches leaf names via wildcard, not the full path it was given, so
the exclusion never matched; and the deferred
"powershell.exe -Command <scriptblock> -ArgumentList <path>" command
line is not valid syntax for binding $path in the child process.
PowerShell reads a script fully into memory before running it, so it
holds no open handle on the file - deleting the install directory
(including this script) while it's still executing is safe. Dropped
the exclude/deferred-process workaround entirely.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
$ErrorActionPreference and Set-StrictMode sat before param(), so
PowerShell parsed 'param(...)' as a call to a command named param
instead of the script's parameter block ("The function or command
was called as if it were a method"). The script never ran. Moved
both statements after the param block; only #Requires and the
comment-based help may precede param().
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Test-ReplicationPermissions previously only scanned Allow ACEs, so an
explicit Deny on the DCSync extended rights (common in hardening
baselines that Deny a broad group and Allow only named service
accounts) was invisible to the pre-flight check: it reported
"verified" while Get-ADReplAccount still failed with "Replication
access was denied". The check now flags exactly which right is
blocked and by which identity's Deny ACE.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DSInternals 7.0 fetches the AD schema via DRS (GetNCChanges) before
replicating accounts, so the schema NC has its own ACL requirement.
- Test-ReplicationPermissions now validates rights on both the
domain NC and the configuration NC (schema NC inherits from it).
- Updated README with dsacls delegation examples and dual-NC
least-privilege requirements.
- Improved 'Replication access was denied' error message to name
both NCs and explain the DSInternals 7.0 change.
- Diagnostic dump now includes SchemaDN.
All versions bumped to unified v2.4.4.
When Get-ADReplAccount or Test-PasswordQuality throws, the catch
block now dumps the full exception chain (type, message, HResult,
source, target site, stack trace, inner exceptions) along with
runtime context (Elysium version, PS version, DSInternals version,
DC, domain, account). Output goes to console and a timestamped
diagnostic file under Reports/ for offline analysis.
Test-ReplicationPermissions:
- Replaced DirectoryEntry.RefreshCache tokenGroups retrieval with
Get-ADUser -Properties tokenGroups. DirectoryEntry does not
understand URI percent-encoding, so the v2.4.1 EscapeDataString
fix caused 'invalid dn syntax' errors.
- Removed EscapeDataString from the ACL DirectoryEntry path as
well; DirectoryEntry expects raw LDAP ADSI path syntax.
All versions bumped to unified v2.4.3.
UTF-8 em-dashes (U+2014) in Elysium.Common.ps1 string literals were
being misinterpreted by Windows PowerShell as containing quote
characters when the file was read without a UTF-8 BOM. This caused
cascading parse errors: unexpected tokens, missing closing braces,
and missing catch blocks.
All em-dashes in .ps1 files have been replaced with ASCII hyphens.
All versions bumped to unified v2.4.2.
Test-ReplicationPermissions and Test-DCClockSkew now escape
Distinguished Names via [System.Uri]::EscapeDataString before
constructing DirectoryEntry LDAP URLs. This prevents URL
mis-parsing when DNs contain /, #, or other reserved characters.
All versions bumped to unified v2.4.1.
Added pre-flight diagnostics:
- Test-DCClockSkew: validates local/DC clock skew before DCSync to
catch Kerberos auth failures early.
- Test-ReplicationPermissions now warns on adminCount=1 (SDProp
protected) and Protected Users group membership (RID 525), both
of which can silently block or revert replication rights.
Fixed DSInternals update flow:
- Replaced Update-Module with Install-Module -Force -AllowClobber
to work around a PowerShellGet null PublishedDate bug.
All versions bumped to unified v2.4.0.
Test-WeakADPasswords.ps1 now validates the installed DSInternals
version at startup:
- v6.2 (unsigned) warns that native DLLs are blocked and replication
will fail; directs operator to Update-Module DSInternals.
- Below v7.0 prompts to auto-update via Update-Module -Force and
exits cleanly so the new version is loaded on re-run.
- v7.0+ passes silently.
All versions bumped to unified v2.3.0.
The Zone.Identifier block detection now dynamically resolves the
actual DSInternals module installation path via Get-Module instead
of hardcoding a ProgramFiles path, so the Unblock-File command in
the error message is always correct.
All versions bumped to unified v2.2.5.
Test-ReplicationPermissions:
- Skip InheritOnly ACEs since they do not apply to the domain root
object itself, only to child objects.
Test-WeakADPasswords:
- Detect Windows Zone.Identifier blocks on DSInternals DLLs and
emit a clear error with the exact Unblock-File remediation
command instead of a vague warning.
All versions bumped to unified v2.2.4.
Test-ReplicationPermissions now recognizes:
- GenericAll as satisfying replication rights
- Blanket ExtendedRight (empty ObjectType) ACEs
Also adds diagnostic hints distinguishing between
'missing ACE entirely' and 'ACE exists but not for you'.
All versions bumped to unified v2.2.3.
Test-ReplicationPermissions now uses the tokenGroups constructed
attribute to resolve all effective SIDs in the caller's Kerberos
token, including nested group memberships. This replaces the
previous MemberOf walk which missed indirect entitlement and
could produce false-positive missing-permission errors.
All versions bumped to unified v2.2.2.
- Add to Elysium.Common.ps1 as the single
runtime source of truth for version strings.
- Update Update-KHDB.ps1 User-Agent to reference .
- Update Test-WeakADPasswords.ps1 usage beacon payload to reference
.
- Add Bump-Version.ps1 release helper that updates the centralized
variable, ASCII headers across .ps1/.py files, runtime references,
and prints a CHANGELOG stub.
Consolidated duplicated helpers into Elysium.Common.ps1:
- Settings parsing (Read-KeyValueSettingsFile, Read-ElysiumSettings, Get-SettingsValue)
- Azure Blob URI builder (Build-BlobUri)
- S3 SigV4 signing helpers and AWS module bootstrap
- AD credential validation and replication permission pre-check
- Parallel execution helper (Get-FunctionDefinitionText)
Test-WeakADPasswords.ps1 and Extract-NTHashes.ps1 now import
Elysium.Common.ps1 for the first time. Update-KHDB.ps1 and
Prepare-KHDBStorage.ps1 removed their local duplicates.
Deleted legacy Settings.ps1 (superseded by ElysiumSettings.txt).
Removed stray placeholder comment in Elysium.ps1.
All versions bumped to unified v2.2.1.
- Unified project versioning (v2.2.0) across all scripts, settings template,
and documentation. All components now share a single version number.
- Added Test-ReplicationPermissions to Test-WeakADPasswords.ps1 to validate
the three required AD replication extended rights before DCSync, providing
clear fail-fast errors when permissions are missing.
- Updated CHANGELOG.md with unified versioning strategy and release history.
- Updated README.md with versioning section and improved troubleshooting docs.