New updates
This commit is contained in:
230
Update-KHDB.ps1
230
Update-KHDB.ps1
@@ -7,7 +7,7 @@
|
||||
##################################################
|
||||
## Project: Elysium ##
|
||||
## File: Update-KHDB.ps1 ##
|
||||
## Version: 1.0.1 ##
|
||||
## Version: 1.1.0 ##
|
||||
## Support: support@cqre.net ##
|
||||
##################################################
|
||||
|
||||
@@ -16,83 +16,183 @@
|
||||
Known hashes database update script for the Elysium AD password testing tool.
|
||||
|
||||
.DESCRIPTION
|
||||
This script downloads khdb.txt.zip from the designated Azure Storage account, decompresses it, and overwrites the current version.
|
||||
This script downloads khdb.txt.zip from the designated Azure Storage account, validates and decompresses it, and atomically updates the current version with backup and logging.
|
||||
#>
|
||||
|
||||
# Initialize an empty hashtable to store settings
|
||||
$ElysiumSettings = @{}
|
||||
# safer defaults
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
# Read the settings file
|
||||
$settingsPath = "ElysiumSettings.txt"
|
||||
Get-Content $settingsPath | ForEach-Object {
|
||||
if ($_ -notmatch '^#' -and $_.Trim()) {
|
||||
$keyValue = $_.Split('=', 2)
|
||||
$key = $keyValue[0].Trim()
|
||||
$value = $keyValue[1].Trim().Trim("'")
|
||||
$ElysiumSettings[$key] = $value
|
||||
# ensure TLS 1.2
|
||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# Resolve paths
|
||||
$scriptRoot = $PSScriptRoot
|
||||
|
||||
function Start-UpdateTranscript {
|
||||
param(
|
||||
[string]$BasePath
|
||||
)
|
||||
try {
|
||||
$logsDir = Join-Path -Path $BasePath -ChildPath 'Reports/logs'
|
||||
if (-not (Test-Path $logsDir)) { New-Item -Path $logsDir -ItemType Directory -Force | Out-Null }
|
||||
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$logPath = Join-Path -Path $logsDir -ChildPath "update-khdb-$ts.log"
|
||||
Start-Transcript -Path $logPath -Force | Out-Null
|
||||
} catch {
|
||||
Write-Warning "Could not start transcript: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
# Verify that all required settings have been loaded
|
||||
if (-not $ElysiumSettings.ContainsKey("storageAccountName") -or
|
||||
-not $ElysiumSettings.ContainsKey("containerName") -or
|
||||
-not $ElysiumSettings.ContainsKey("sasToken")) {
|
||||
Write-Error "Missing required settings. Please check your settings file."
|
||||
return
|
||||
function Stop-UpdateTranscript {
|
||||
try { Stop-Transcript | Out-Null } catch {}
|
||||
}
|
||||
|
||||
# Construct the full URL for accessing the Azure Blob Storage
|
||||
$storageAccountName = $ElysiumSettings["storageAccountName"]
|
||||
$containerName = $ElysiumSettings["containerName"]
|
||||
$sasToken = $ElysiumSettings["sasToken"]
|
||||
$AzureBlobStorageUrl = "https://$storageAccountName.blob.core.windows.net/$containerName/khdb.txt.zip$sasToken"
|
||||
|
||||
# Load necessary .NET assembly for HTTP operations
|
||||
Add-Type -AssemblyName System.Net.Http
|
||||
function Update-KHDB {
|
||||
Write-Host "Downloading KHDB..."
|
||||
|
||||
# Initialize the client for downloading the file
|
||||
$httpClient = New-Object System.Net.Http.HttpClient
|
||||
|
||||
try {
|
||||
# Start the asynchronous request to download the file
|
||||
$response = $httpClient.GetAsync($AzureBlobStorageUrl, [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead).Result
|
||||
|
||||
if ($response.IsSuccessStatusCode) {
|
||||
$totalBytes = $response.Content.Headers.ContentLength
|
||||
$totalRead = 0
|
||||
$read = 0
|
||||
$buffer = New-Object byte[] 8192
|
||||
$stream = $response.Content.ReadAsStreamAsync().Result
|
||||
$fileStream = [System.IO.File]::Create("khdb.txt.zip")
|
||||
|
||||
# Read the stream in chunks and update the progress bar
|
||||
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
|
||||
$fileStream.Write($buffer, 0, $read)
|
||||
$totalRead += $read
|
||||
$percentage = ($totalRead * 100) / $totalBytes
|
||||
Write-Progress -Activity "Downloading khdb.txt.zip" -Status "$([Math]::Round($percentage, 2))% Complete:" -PercentComplete $percentage
|
||||
}
|
||||
|
||||
$fileStream.Close()
|
||||
Write-Host "KHDB.zip downloaded successfully."
|
||||
} else {
|
||||
Write-Error "Failed to download khdb.txt.zip: $($response.StatusCode)"
|
||||
function Read-ElysiumSettings {
|
||||
$settings = @{}
|
||||
$settingsPath = Join-Path -Path $scriptRoot -ChildPath 'ElysiumSettings.txt'
|
||||
if (-not (Test-Path $settingsPath)) { throw "Settings file not found at $settingsPath" }
|
||||
Get-Content $settingsPath | ForEach-Object {
|
||||
if ($_ -and -not $_.Trim().StartsWith('#')) {
|
||||
$kv = $_ -split '=', 2
|
||||
if ($kv.Count -eq 2) { $settings[$kv[0].Trim()] = $kv[1].Trim().Trim("'") }
|
||||
}
|
||||
} catch {
|
||||
Write-Error "Error during download: $_"
|
||||
return
|
||||
}
|
||||
return $settings
|
||||
}
|
||||
|
||||
# Decompressing KHDB.zip
|
||||
function Get-InstallationPath([hashtable]$settings) {
|
||||
$p = $settings['InstallationPath']
|
||||
if ([string]::IsNullOrWhiteSpace($p)) { return $scriptRoot }
|
||||
if ([System.IO.Path]::IsPathRooted($p)) { return $p }
|
||||
return (Join-Path -Path $scriptRoot -ChildPath $p)
|
||||
}
|
||||
|
||||
function New-HttpClient {
|
||||
Add-Type -AssemblyName System.Net.Http
|
||||
$client = [System.Net.Http.HttpClient]::new()
|
||||
$client.Timeout = [TimeSpan]::FromSeconds(600)
|
||||
$client.DefaultRequestHeaders.UserAgent.ParseAdd('Elysium/1.1 (+Update-KHDB)')
|
||||
return $client
|
||||
}
|
||||
|
||||
function Build-BlobUri([string]$account, [string]$container, [string]$sas) {
|
||||
if ([string]::IsNullOrWhiteSpace($account)) { throw 'storageAccountName is missing or empty.' }
|
||||
if ([string]::IsNullOrWhiteSpace($container)) { throw 'containerName is missing or empty.' }
|
||||
if ([string]::IsNullOrWhiteSpace($sas)) { throw 'sasToken is missing or empty.' }
|
||||
$sas = $sas.Trim()
|
||||
if (-not $sas.StartsWith('?')) { $sas = '?' + $sas }
|
||||
$ub = [System.UriBuilder]::new("https://$account.blob.core.windows.net/$container/khdb.txt.zip")
|
||||
$ub.Query = $sas.TrimStart('?')
|
||||
return $ub.Uri.AbsoluteUri
|
||||
}
|
||||
|
||||
function Invoke-DownloadWithRetry([System.Net.Http.HttpClient]$client, [string]$uri, [string]$targetPath) {
|
||||
$retries = 5
|
||||
$delay = 2
|
||||
for ($i = 0; $i -lt $retries; $i++) {
|
||||
try {
|
||||
$resp = $client.GetAsync($uri, [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead).Result
|
||||
if (-not $resp.IsSuccessStatusCode) {
|
||||
$code = [int]$resp.StatusCode
|
||||
if (($code -ge 500 -and $code -lt 600) -or $code -eq 429 -or $code -eq 408) { throw "Transient HTTP error $code" }
|
||||
throw "HTTP error $code"
|
||||
}
|
||||
$totalBytes = $resp.Content.Headers.ContentLength
|
||||
$stream = $resp.Content.ReadAsStreamAsync().Result
|
||||
$fs = [System.IO.File]::Create($targetPath)
|
||||
try {
|
||||
$buffer = New-Object byte[] 8192
|
||||
$totalRead = 0
|
||||
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
|
||||
$fs.Write($buffer, 0, $read)
|
||||
$totalRead += $read
|
||||
if ($totalBytes) {
|
||||
$pct = ($totalRead * 100.0) / $totalBytes
|
||||
Write-Progress -Activity "Downloading khdb.txt.zip" -Status ("{0:N2}% Complete" -f $pct) -PercentComplete $pct
|
||||
} else {
|
||||
Write-Progress -Activity "Downloading khdb.txt.zip" -Status ("Downloaded {0:N0} bytes" -f $totalRead) -PercentComplete 0
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
$fs.Close(); $stream.Close()
|
||||
}
|
||||
return
|
||||
} catch {
|
||||
if ($i -lt ($retries - 1)) {
|
||||
Write-Warning "Download failed (attempt $($i+1)/$retries): $($_.Exception.Message). Retrying in ${delay}s..."
|
||||
Start-Sleep -Seconds $delay
|
||||
$delay = [Math]::Min($delay * 2, 30)
|
||||
} else {
|
||||
throw
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Validate-KHDBFile([string]$path) {
|
||||
if (-not (Test-Path $path)) { throw "Validation failed: $path not found" }
|
||||
$lines = Get-Content -Path $path -Encoding UTF8
|
||||
if (-not $lines -or $lines.Count -eq 0) { throw 'Validation failed: file is empty.' }
|
||||
$regex = '^[0-9A-Fa-f]{32}$'
|
||||
$invalid = $lines | Where-Object { $_ -notmatch $regex }
|
||||
if ($invalid.Count -gt 0) {
|
||||
throw ("Validation failed: {0} invalid lines detected." -f $invalid.Count)
|
||||
}
|
||||
# Deduplicate and normalize line endings
|
||||
$unique = $lines | ForEach-Object { $_.Trim() } | Where-Object { $_ } | Sort-Object -Unique
|
||||
Set-Content -Path $path -Value $unique -Encoding ASCII
|
||||
}
|
||||
|
||||
function Update-KHDB {
|
||||
Start-UpdateTranscript -BasePath $scriptRoot
|
||||
try {
|
||||
Expand-Archive -Path "khdb.txt.zip" -DestinationPath . -Force
|
||||
Remove-Item -Path "khdb.txt.zip" -Force # Delete the zip file after extraction
|
||||
Write-Host "KHDB decompressed and cleaned up successfully."
|
||||
$settings = Read-ElysiumSettings
|
||||
$installPath = Get-InstallationPath $settings
|
||||
if (-not (Test-Path $installPath)) { New-Item -Path $installPath -ItemType Directory -Force | Out-Null }
|
||||
|
||||
$storageAccountName = $settings['storageAccountName']
|
||||
$containerName = $settings['containerName']
|
||||
$sasToken = $settings['sasToken']
|
||||
$uri = Build-BlobUri -account $storageAccountName -container $containerName -sas $sasToken
|
||||
|
||||
$client = New-HttpClient
|
||||
$tmpDir = New-Item -ItemType Directory -Path ([System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "elysium-khdb-" + [System.Guid]::NewGuid())) -Force
|
||||
$zipPath = Join-Path -Path $tmpDir.FullName -ChildPath 'khdb.txt.zip'
|
||||
$extractDir = Join-Path -Path $tmpDir.FullName -ChildPath 'extract'
|
||||
New-Item -ItemType Directory -Path $extractDir -Force | Out-Null
|
||||
|
||||
Write-Host "Downloading KHDB from Azure Blob Storage..."
|
||||
Invoke-DownloadWithRetry -client $client -uri $uri -targetPath $zipPath
|
||||
Write-Host "Download completed. Extracting archive..."
|
||||
|
||||
Expand-Archive -Path $zipPath -DestinationPath $extractDir -Force
|
||||
|
||||
$extractedKHDB = Get-ChildItem -Path $extractDir -Recurse -Filter 'khdb.txt' | Select-Object -First 1
|
||||
if (-not $extractedKHDB) { throw 'Extracted archive does not contain khdb.txt.' }
|
||||
|
||||
# Validate content
|
||||
Validate-KHDBFile -path $extractedKHDB.FullName
|
||||
|
||||
# Compute target path and backup
|
||||
$targetKHDB = Join-Path -Path $installPath -ChildPath 'khdb.txt'
|
||||
if (Test-Path $targetKHDB) {
|
||||
$ts = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$backupPath = Join-Path -Path $installPath -ChildPath ("khdb.txt.bak-$ts")
|
||||
Copy-Item -Path $targetKHDB -Destination $backupPath -Force
|
||||
Write-Host "Existing KHDB backed up to $backupPath"
|
||||
}
|
||||
|
||||
# Atomic-ish replace: move validated file into place
|
||||
Move-Item -Path $extractedKHDB.FullName -Destination $targetKHDB -Force
|
||||
Write-Host "KHDB updated at $targetKHDB"
|
||||
Write-Host "KHDB update completed successfully."
|
||||
} catch {
|
||||
Write-Error "Error decompressing KHDB: $_"
|
||||
return
|
||||
Write-Error ("KHDB update failed: {0}" -f $_.Exception.Message)
|
||||
throw
|
||||
} finally {
|
||||
try { if ($tmpDir -and (Test-Path $tmpDir.FullName)) { Remove-Item -Path $tmpDir.FullName -Recurse -Force } } catch {}
|
||||
Stop-UpdateTranscript
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user