noid-privacy/Tools/Parse-EdgeBaseline.ps1

302 lines
9.9 KiB
PowerShell

<#
.SYNOPSIS
Parse Microsoft Edge Security Baseline GPO files to JSON (DEVELOPER TOOL ONLY)
.DESCRIPTION
**NOTE: This is a DEVELOPER/MAINTENANCE tool - NOT needed for production use!**
Parses GPO backups from Microsoft Edge Security Baseline:
- Registry.pol (Computer policies for Microsoft Edge)
Outputs structured JSON files for Edge hardening settings.
.PARAMETER BaselinePath
Path to Microsoft Edge Security Baseline folder
.PARAMETER OutputPath
Path where JSON output files will be saved
.NOTES
Author: NexusOne23
Version: 2.2.1
Requires: PowerShell 5.1+
.EXAMPLE
.\Parse-EdgeBaseline.ps1 -BaselinePath "C:\Edge Baseline" -OutputPath ".\Modules\EdgeHardening\ParsedSettings"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$BaselinePath,
[Parameter(Mandatory = $false)]
[string]$OutputPath = (Join-Path $PSScriptRoot "..\Modules\EdgeHardening\ParsedSettings")
)
#region Helper Functions
function Read-PolFile {
<#
.SYNOPSIS
Parse binary Registry.pol file
.DESCRIPTION
Based on Microsoft GPRegistryPolicyParser format
Registry.pol binary format:
- Signature: PReg (4 bytes)
- Version: 1 (4 bytes)
- Entries: [KeyName;ValueName;Type;Size;Data]
#>
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
if (-not (Test-Path $Path)) {
Write-Warning "Registry.pol not found: $Path"
return @()
}
try {
$entries = @()
$bytes = [System.IO.File]::ReadAllBytes($Path)
# Check signature (PReg)
$signature = [System.Text.Encoding]::ASCII.GetString($bytes[0..3])
if ($signature -ne 'PReg') {
Write-Warning "Invalid Registry.pol signature: $signature"
return @()
}
# Check version
$version = [BitConverter]::ToInt32($bytes, 4)
if ($version -ne 1) {
Write-Warning "Unsupported Registry.pol version: $version"
return @()
}
$index = 8 # Start after signature and version
while ($index -lt $bytes.Length) {
# Read entry: [KeyName;ValueName;Type;Size;Data]
# Read KeyName (Unicode null-terminated string)
$keyNameBytes = @()
while ($index -lt ($bytes.Length - 1)) {
$b1 = $bytes[$index]
$b2 = $bytes[$index + 1]
$index += 2
if ($b1 -eq 0 -and $b2 -eq 0) {
break
}
$keyNameBytes += $b1, $b2
}
$keyName = [System.Text.Encoding]::Unicode.GetString($keyNameBytes)
# Skip semicolon
$index += 2
# Read ValueName (Unicode null-terminated string)
$valueNameBytes = @()
while ($index -lt ($bytes.Length - 1)) {
$b1 = $bytes[$index]
$b2 = $bytes[$index + 1]
$index += 2
if ($b1 -eq 0 -and $b2 -eq 0) {
break
}
$valueNameBytes += $b1, $b2
}
$valueName = [System.Text.Encoding]::Unicode.GetString($valueNameBytes)
# Skip semicolon
$index += 2
# Read Type (DWORD - 4 bytes)
if ($index + 4 -gt $bytes.Length) { break }
$type = [BitConverter]::ToInt32($bytes, $index)
$index += 4
# Skip semicolon
$index += 2
# Read Size (DWORD - 4 bytes)
if ($index + 4 -gt $bytes.Length) { break }
$size = [BitConverter]::ToInt32($bytes, $index)
$index += 4
# Skip semicolon
$index += 2
# Read Data
$data = $null
if ($size -gt 0 -and ($index + $size) -le $bytes.Length) {
$dataBytes = $bytes[$index..($index + $size - 1)]
# Parse based on type
switch ($type) {
1 { # REG_SZ (String)
$data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0)
}
2 { # REG_EXPAND_SZ
$data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0)
}
3 { # REG_BINARY
$data = $dataBytes
}
4 { # REG_DWORD
if ($dataBytes.Length -ge 4) {
$data = [BitConverter]::ToInt32($dataBytes, 0)
}
}
7 { # REG_MULTI_SZ
$data = [System.Text.Encoding]::Unicode.GetString($dataBytes).TrimEnd([char]0) -split '\x00'
}
default {
$data = $dataBytes
}
}
$index += $size
}
# Skip closing bracket
$index += 2
# Add entry
$entries += [PSCustomObject]@{
KeyName = $keyName
ValueName = $valueName
Type = switch ($type) {
1 { "REG_SZ" }
2 { "REG_EXPAND_SZ" }
3 { "REG_BINARY" }
4 { "REG_DWORD" }
7 { "REG_MULTI_SZ" }
11 { "REG_QWORD" }
default { "Unknown($type)" }
}
Data = $data
}
}
return $entries
}
catch {
Write-Error "Failed to parse Registry.pol: $Path - $_"
return @()
}
}
#endregion
#region Main Parsing Logic
Write-Host ""
Write-Host "================================================" -ForegroundColor Cyan
Write-Host " Microsoft Edge Security Baseline Parser" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan
Write-Host ""
# Validate baseline path
if (-not (Test-Path $BaselinePath)) {
Write-Error "Baseline path not found: $BaselinePath"
exit 1
}
# Ensure output directory exists
if (-not (Test-Path $OutputPath)) {
New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
Write-Host "Created output directory: $OutputPath" -ForegroundColor Green
}
# Find GPO folder (should be only one GPO in Edge Baseline)
$gpoFolders = Get-ChildItem -Path (Join-Path $BaselinePath "GPOs") -Directory
if ($gpoFolders.Count -eq 0) {
Write-Error "No GPO folders found in $BaselinePath\GPOs"
exit 1
}
Write-Host "Found $($gpoFolders.Count) GPO folder(s)" -ForegroundColor Cyan
Write-Host ""
$allComputerPolicies = @()
foreach ($gpoFolder in $gpoFolders) {
$gpoName = $gpoFolder.Name
Write-Host "Processing GPO: $gpoName" -ForegroundColor Yellow
# Parse Computer Registry policies
$computerPolPath = Join-Path $gpoFolder.FullName "DomainSysvol\GPO\Machine\registry.pol"
if (Test-Path $computerPolPath) {
Write-Host " Parsing Computer registry policies..." -ForegroundColor Gray
$computerPolicies = Read-PolFile -Path $computerPolPath
if ($computerPolicies.Count -gt 0) {
$allComputerPolicies += $computerPolicies
Write-Host " Found $($computerPolicies.Count) Computer registry policies" -ForegroundColor Green
}
else {
Write-Warning " No Computer registry policies found"
}
}
else {
Write-Warning " Computer registry.pol not found"
}
# Check for User policies (Edge baseline typically doesn't have user policies)
$userPolPath = Join-Path $gpoFolder.FullName "DomainSysvol\GPO\User\registry.pol"
if (Test-Path $userPolPath) {
Write-Host " User registry.pol found (unexpected for Edge baseline)" -ForegroundColor Yellow
}
Write-Host ""
}
# Save Computer Registry Policies
if ($allComputerPolicies.Count -gt 0) {
$computerPoliciesFile = Join-Path $OutputPath "EdgePolicies.json"
$allComputerPolicies | ConvertTo-Json -Depth 10 | Out-File -FilePath $computerPoliciesFile -Encoding UTF8 -Force
Write-Host "Saved $($allComputerPolicies.Count) policies to: EdgePolicies.json" -ForegroundColor Green
}
# Create summary
$summary = [PSCustomObject]@{
TotalEdgePolicies = $allComputerPolicies.Count
ParsedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
BaselineVersion = "Edge v139"
RegistryPaths = ($allComputerPolicies | Select-Object -ExpandProperty KeyName -Unique | Sort-Object)
}
$summaryFile = Join-Path $OutputPath "Summary.json"
$summary | ConvertTo-Json -Depth 10 | Out-File -FilePath $summaryFile -Encoding UTF8 -Force
Write-Host ""
Write-Host "================================================" -ForegroundColor Cyan
Write-Host " Parsing Complete" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "Total Edge Policies: $($allComputerPolicies.Count)" -ForegroundColor White
Write-Host ""
Write-Host "Output files:" -ForegroundColor White
Write-Host " - EdgePolicies.json" -ForegroundColor Gray
Write-Host " - Summary.json" -ForegroundColor Gray
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host " 1. Review parsed policies in EdgePolicies.json" -ForegroundColor Gray
Write-Host " 2. Implement Set-EdgePolicies.ps1 (native PowerShell)" -ForegroundColor Gray
Write-Host " 3. Implement Test-EdgePolicies.ps1 (compliance check)" -ForegroundColor Gray
Write-Host " 4. Implement Invoke-EdgeHardening.ps1 (main entry point)" -ForegroundColor Gray
Write-Host ""
#endregion