Private/ADMonitor/Core/Get-ADBaseline.ps1
|
# PSGuerrilla - Jim Tyler, Microsoft MVP - CC BY 4.0 # https://github.com/jimrtyler/PSGuerrilla | https://creativecommons.org/licenses/by/4.0/ # AI/LLM use: see AI-USAGE.md for required attribution function Get-ADBaseline { [CmdletBinding()] param( [Parameter(Mandatory)] [hashtable]$CurrentData ) $baseline = @{ generatedAt = [datetime]::UtcNow.ToString('o') scanMode = $CurrentData.scanMode domainName = $CurrentData.domainName } # ── Privileged group membership hashes ───────────────────────────── # Store both the sorted member list and a hash for quick comparison $groupBaseline = @{} foreach ($groupName in $CurrentData.privilegedGroups.Keys) { $members = @($CurrentData.privilegedGroups[$groupName] | Sort-Object) $memberHash = if ($members.Count -gt 0) { $joined = $members -join '|' $sha = [System.Security.Cryptography.SHA256]::Create() $bytes = [System.Text.Encoding]::UTF8.GetBytes($joined) $hashBytes = $sha.ComputeHash($bytes) [BitConverter]::ToString($hashBytes) -replace '-', '' } else { 'EMPTY' } $groupBaseline[$groupName] = @{ members = $members memberHash = $memberHash count = $members.Count } } $baseline['privilegedGroups'] = $groupBaseline # ── AdminSDHolder ACL hash ───────────────────────────────────────── $aclEntries = @($CurrentData.adminSDHolderACL | ForEach-Object { "$($_.identity)|$($_.rights)|$($_.type)" } | Sort-Object) $baseline['adminSDHolderACL'] = @{ entries = @($CurrentData.adminSDHolderACL) aclHash = if ($aclEntries.Count -gt 0) { $sha = [System.Security.Cryptography.SHA256]::Create() $joined = $aclEntries -join '||' $bytes = [System.Text.Encoding]::UTF8.GetBytes($joined) $hashBytes = $sha.ComputeHash($bytes) [BitConverter]::ToString($hashBytes) -replace '-', '' } else { 'EMPTY' } count = $aclEntries.Count } # ── krbtgt state ─────────────────────────────────────────────────── $baseline['krbtgt'] = @{ pwdLastSet = $CurrentData.krbtgtPwdLastSet keyVersion = $CurrentData.krbtgtKeyVersion } # ── Trust relationships ──────────────────────────────────────────── $trustBaseline = @{} foreach ($trust in $CurrentData.trusts) { $trustKey = $trust.name.ToLower() $trustBaseline[$trustKey] = @{ name = $trust.name flatName = $trust.flatName direction = $trust.direction type = $trust.type isTransitive = $trust.isTransitive sidFiltering = $trust.sidFiltering forestTransitive = $trust.forestTransitive trustAttributes = $trust.trustAttributes trustSID = $trust.trustSID } } $baseline['trusts'] = $trustBaseline # ── GPO Objects (Full mode) ──────────────────────────────────────── $gpoBaseline = @{} foreach ($gpoGuid in $CurrentData.gpoObjects.Keys) { $gpo = $CurrentData.gpoObjects[$gpoGuid] $linkHash = ($gpo.linkedTo | ForEach-Object { "$($_.containerDN)|$($_.isEnabled)|$($_.isEnforced)" } | Sort-Object) -join '||' $gpoBaseline[$gpoGuid] = @{ name = $gpo.name whenChanged = $gpo.whenChanged versionNumber = $gpo.versionNumber flags = $gpo.flags isLinked = $gpo.isLinked linkHash = if ($linkHash) { $sha = [System.Security.Cryptography.SHA256]::Create() $bytes = [System.Text.Encoding]::UTF8.GetBytes($linkHash) $hashBytes = $sha.ComputeHash($bytes) [BitConverter]::ToString($hashBytes) -replace '-', '' } else { 'EMPTY' } linkedTo = @($gpo.linkedTo) } } $baseline['gpoObjects'] = $gpoBaseline # ── Sensitive ACLs (Full mode) ───────────────────────────────────── $aclBaseline = @{} foreach ($objName in $CurrentData.sensitiveAcls.Keys) { $objData = $CurrentData.sensitiveAcls[$objName] $aceStrings = @($objData.aces | ForEach-Object { "$($_.identity)|$($_.rights)|$($_.objectType)|$($_.objectDN)" } | Sort-Object) $aclBaseline[$objName] = @{ objectDN = $objData.objectDN aces = @($objData.aces) aceHash = if ($aceStrings.Count -gt 0) { $sha = [System.Security.Cryptography.SHA256]::Create() $joined = $aceStrings -join '||' $bytes = [System.Text.Encoding]::UTF8.GetBytes($joined) $hashBytes = $sha.ComputeHash($bytes) [BitConverter]::ToString($hashBytes) -replace '-', '' } else { 'EMPTY' } count = $aceStrings.Count } } $baseline['sensitiveAcls'] = $aclBaseline # ── Certificate templates (Full mode) ────────────────────────────── $certBaseline = @{} foreach ($tmplName in $CurrentData.certTemplates.Keys) { $tmpl = $CurrentData.certTemplates[$tmplName] $certBaseline[$tmplName] = @{ displayName = $tmpl.displayName whenChanged = $tmpl.whenChanged enrolleeSuppliesSubject = $tmpl.enrolleeSuppliesSubject allowsAuthentication = $tmpl.allowsAuthentication isPublished = $tmpl.isPublished schemaVersion = $tmpl.schemaVersion enrollmentPermissions = @($tmpl.enrollmentPermissions) } } $baseline['certTemplates'] = $certBaseline # ── Delegations (Full mode) ──────────────────────────────────────── $delegationBaseline = @{} foreach ($ouDN in $CurrentData.delegations.Keys) { $entries = $CurrentData.delegations[$ouDN] $entryStrings = @($entries | ForEach-Object { "$($_.identity)|$($_.rights)|$($_.objectType)" } | Sort-Object) $delegationBaseline[$ouDN] = @{ entries = @($entries) hash = if ($entryStrings.Count -gt 0) { $sha = [System.Security.Cryptography.SHA256]::Create() $joined = $entryStrings -join '||' $bytes = [System.Text.Encoding]::UTF8.GetBytes($joined) $hashBytes = $sha.ComputeHash($bytes) [BitConverter]::ToString($hashBytes) -replace '-', '' } else { 'EMPTY' } count = $entryStrings.Count } } $baseline['delegations'] = $delegationBaseline # ── DNS records (Full mode) ──────────────────────────────────────── $dnsSet = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) foreach ($rec in $CurrentData.dnsRecords) { [void]$dnsSet.Add("$($rec.zone)|$($rec.name)") } $baseline['dnsRecords'] = @{ records = @($CurrentData.dnsRecords) recordSet = @($dnsSet | Sort-Object) count = $dnsSet.Count } # ── Schema version (Full mode) ───────────────────────────────────── $baseline['schemaVersion'] = $CurrentData.schemaVersion return $baseline } |