mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-09-29 18:44:31 +02:00
* Add guarded creation of disabled unlinked audit GPOs * Reference PR 427 in GPO creation changelogs * Accept only inert native ADM placeholders and fix PS5 JSON fixture * Preserve fractional UTC strings in existing probe fixtures
73 lines
6.1 KiB
PowerShell
73 lines
6.1 KiB
PowerShell
# Genuine Microsoft backup read + native workgroup refusal; no domain creation/import is attempted.
|
|
param([Parameter(Mandatory)][string]$OutputPath,[switch]$AllowHostedGpmcInstall)
|
|
$ErrorActionPreference='Stop'
|
|
if($env:OS -ne 'Windows_NT') {throw 'Windows is required.'}
|
|
$repo=Split-Path $PSScriptRoot -Parent
|
|
Import-Module (Join-Path $repo 'modules/AuditProfiles.psm1') -Force
|
|
. (Join-Path $repo 'scripts/Configuration.ps1')
|
|
. (Join-Path $repo 'scripts/GpoAuditPackages.ps1')
|
|
. (Join-Path $repo 'scripts/EvtxRecovery.ps1')
|
|
. (Join-Path $repo 'scripts/AdObjectSacl.ps1')
|
|
. (Join-Path $repo 'scripts/GpoCreation.ps1')
|
|
$script:checks=0
|
|
function Assert($Value,[string]$Message){if(-not $Value){throw "FAIL: $Message"};$script:checks++}
|
|
function Reject([scriptblock]$Code,[string]$Pattern){$message='';try{& $Code|Out-Null}catch{$message=$_.Exception.Message};Assert ($message -match $Pattern) "Expected $Pattern, got $message"}
|
|
function New-GPO {throw 'Forbidden domain mutation in read-only native test.'}
|
|
function Import-WelaGpoNativeTarget {throw 'Forbidden domain import in read-only native test.'}
|
|
$computer=Get-CimInstance Win32_ComputerSystem -ErrorAction Stop
|
|
Assert ($computer.PartOfDomain -eq $false) 'Native negative test requires an actual workgroup host'
|
|
if($AllowHostedGpmcInstall) {
|
|
if($env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_ENVIRONMENT -ne 'github-hosted') {throw 'GPMC installation is restricted to explicitly opted-in disposable GitHub-hosted runners.'}
|
|
Import-Module ServerManager -ErrorAction Stop
|
|
if((Get-WindowsFeature GPMC).Installed -ne $true) {
|
|
$installed=Install-WindowsFeature GPMC -ErrorAction Stop
|
|
if(-not $installed.Success -or [string]$installed.RestartNeeded -ne 'No') {throw 'GPMC installation requires successful completion without a pending restart; native evidence is blocked.'}
|
|
}
|
|
}
|
|
$null=New-WelaGpm
|
|
$before=Get-WelaEffectiveAuditPolicy;$precedenceBefore=Get-WelaRegistryState 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' SCENoApplyLegacyAuditPolicy
|
|
$root=Resolve-WelaEvtxPath $OutputPath
|
|
if(Test-Path $root){throw 'Use a fresh native artifact directory.'}
|
|
$null=New-Item -ItemType Directory $root;Protect-WelaGpoOutput $root
|
|
try {
|
|
$url='https://download.microsoft.com/download/8/5/c/85c25433-a1b0-4ffa-9429-7e023e7da8d8/Windows%20Server%202022%20Security%20Baseline.zip'
|
|
$expectedHash='49590cc694626d171fc934fafea6494f13ecd3843086704b7a5b98355909b8e0'
|
|
$zipPath=Join-Path $root 'sct.zip'
|
|
[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12
|
|
Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing -ErrorAction Stop
|
|
Assert ((Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash.ToLowerInvariant() -ceq $expectedHash) 'Official SCT archive matches the reviewed SHA-256 pin'
|
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
$extract=Join-Path $root 'sct';$null=New-Item -ItemType Directory $extract
|
|
$zip=[IO.Compression.ZipFile]::OpenRead($zipPath)
|
|
try {
|
|
foreach($entry in $zip.Entries) {
|
|
$destination=[IO.Path]::GetFullPath((Join-Path $extract $entry.FullName))
|
|
if(-not $destination.StartsWith($extract+[IO.Path]::DirectorySeparatorChar,[StringComparison]::OrdinalIgnoreCase)) {throw 'Archive traversal refused.'}
|
|
}
|
|
} finally {$zip.Dispose()}
|
|
[IO.Compression.ZipFile]::ExtractToDirectory($zipPath,$extract)
|
|
$backupRoot=Join-Path $extract 'Windows Server-2022-Security-Baseline-FINAL/GPOs'
|
|
$backupId='20fad6fb-7c6d-496e-801c-0434769847ff'
|
|
$native=Get-WelaGpoNativeBackup $backupRoot $backupId
|
|
$doc=Read-WelaGpoXml $native.Xml
|
|
$sourceId=Get-WelaGpoGuid (Get-WelaGpoText $doc.DocumentElement.Identifier 'Identifier' 'http://www.microsoft.com/GroupPolicy/Types')
|
|
Assert ($sourceId -eq 'fa0f36d8-14ce-4d94-90f7-66a01ddb07c4' -and $sourceId -ne $backupId) 'Native GPMC reads the genuine selected backup instance and distinct source GPO GUID'
|
|
Assert ((Get-WelaGpoText $doc.DocumentElement 'Name') -ceq 'MSFT Windows Server 2022 - Member Server') 'Native report retains official source identity'
|
|
[IO.File]::WriteAllText((Join-Path $root 'native-backup-report.xml'),$native.Xml,[Text.Encoding]::Unicode)
|
|
$package=Join-Path $root 'package';$null=Export-WelaGpoPackage (Get-WelaGpoPackagePlan wela-2.2.0 MemberServer 20348) $package
|
|
$config=[ordered]@{SchemaVersion=1;PackagePath=$package;BackupRoot=$backupRoot;BackupId=$backupId;Domain='example.test';DomainGuid='dddddddd-dddd-dddd-dddd-dddddddddddd';Dc='dc.example.test';Name='Read Only Native Refusal';ReviewedSha256=''}
|
|
$configPath=Join-Path $root 'create.json';[IO.File]::WriteAllText($configPath,($config|ConvertTo-Json),[Text.UTF8Encoding]::new($false))
|
|
Reject {Invoke-WelaGpoCreateCommand -ConfigPath $configPath} 'narrow audit payload'
|
|
Reject {Open-WelaGpoCreationSession ([pscustomobject]$config)} 'workgroup'
|
|
Reject {Get-WelaGpoNativeBackup $backupRoot '11111111-1111-1111-1111-111111111111'} '.'
|
|
# The source files and overall-status method are actual native evidence; nothing is imported.
|
|
Write-WelaGpoReceipt $root 'native-evidence.json' ([ordered]@{ArchiveUrl=$url;ArchiveSha256=$expectedHash;BackupId=$backupId;SourceGpoId=$sourceId;PowerShell=$PSVersionTable.PSVersion.ToString();NativeBackupReportObserved=$true;BroadPayloadRefused=$true;WorkgroupGuardObserved=$true;DomainCreationTested=$false;DomainImportTested=$false;DeploymentVerified=$false})
|
|
} finally {
|
|
$after=Get-WelaEffectiveAuditPolicy;$precedenceAfter=Get-WelaRegistryState 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' SCENoApplyLegacyAuditPolicy
|
|
$beforeKey=@($before.Keys|Sort-Object|ForEach-Object {$_+'='+$before[$_]}) -join ';';$afterKey=@($after.Keys|Sort-Object|ForEach-Object {$_+'='+$after[$_]}) -join ';'
|
|
Assert ($beforeKey -ceq $afterKey) 'All actual native audit masks remain unchanged'
|
|
Assert (($precedenceBefore|ConvertTo-Json -Compress) -ceq ($precedenceAfter|ConvertTo-Json -Compress)) 'Actual precedence state remains unchanged'
|
|
}
|
|
Write-Host "GPO creation native checks: $script:checks passed. Genuine GPMC backup read and negative workgroup/broad-policy checks only; positive domain import remains pending."
|
|
$global:LASTEXITCODE=0
|