Last active
September 11, 2024 17:45
-
-
Save nullbind/c0cee65a281d30e4d53b618f026ccd12 to your computer and use it in GitHub Desktop.
Get-WinProxyInfo.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Work in progress | |
# Automation goals | |
# 1 enumeration HTTP proxy configurations on Windows and AD domain joined systems | |
# 2 parse the proxies | |
# 3 test for unauthenticated outbound internet access. | |
# 4 produce inventory of available proxies and if auth is requires (proxy_url, proxy_port, proxy_source, authentication_required) | |
# ---------------------------------- | |
# Get-HttpProxyInfo | |
# ---------------------------------- | |
# Scott Sutherland (@_nullbind), NETSPI | |
# review: https://github.com/WAftring/NetScripts/blob/3965e8b1e7cc8502bce3ea1a774b7856b15c0c5b/Test-Proxy.ps1 | |
# review: https://github.com/rgl/squid-cache-vagrant/blob/d4cb88ddc90499fe1ad1da6e9551b1abd44dda71/provision-windows-proxy.ps1 | |
# review: https://github.com/theaquamarine/pacfiles/blob/bb65d99e0cf5c45d6c40c3f65da0385347492b66/pacparser/pacparser.cs | |
<# | |
url – The full URL being accessed in web browser. (http:// or https:// or ftp://) | |
host – The hostname from the above url. port numbers and sub-location is not included in this | |
return – Return value can be any of the following | |
DIRECT – Redirects requests directly to the destination | |
PROXY host:port – Redirects requests to Proxy server | |
SOCKS host:port – Redirects requests to SOCKS server | |
add wpad, add the ipv6 version | |
note for change proxy programmatically, netsh winhttp set proxy="127.0.0.1:9000" "10.*,172.*,192.168.*" + powershell | |
notes for presidence | |
#> | |
Function Get-HttpProxyInfo | |
{ | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Set specific domain. This is set to the userdnsdomain by default')] | |
[string]$Domain, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Set specific domain controller. This is set to the logon server by default.')] | |
[string]$DomainController, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'User to authenticate to DC with.')] | |
[string]$Username, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Password to authenticate to DC with.')] | |
[string]$Password | |
) | |
Process | |
{ | |
# --------------------------------------------- | |
# Get Proxy Settings: ProxyServer User | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking ProxyServer user settings" | |
# Get proxy file | |
$ProxyServer = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyServer" -ErrorAction SilentlyContinue | |
# Get proxy status | |
$ProxyEnable = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -ErrorAction SilentlyContinue | |
# Display object | |
if($ProxyServer -ne "") | |
{ | |
$ProxyServerV = $ProxyServer | Select ProxyServer -ExpandProperty ProxyServer | |
$ProxyEnableV = $ProxyEnable | Select ProxyEnable -ExpandProperty ProxyEnable | |
Write-Verbose " [*] ProxyServer user settings:" | |
Write-Verbose " [*] - Registry Key : HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Setting\" | |
Write-Verbose " [*] - Registry Property : ProxyServer" | |
Write-Verbose " [*] - Proxy Server : $ProxyServerV" | |
Write-Verbose " [*] - Proxy Enabled : $ProxyEnableV" | |
}else{ | |
Write-Verbose " [*] - ProxyServer user settings not found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: ProxyServer Machine | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking ProxyServer machine settings" | |
# Get proxy file | |
$ProxyServer = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyServer" -ErrorAction SilentlyContinue | |
# Get proxy status | |
$ProxyEnable = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -ErrorAction SilentlyContinue | |
# Display object | |
if($ProxyServer -ne "") | |
{ | |
$ProxyServerV = $ProxyServer | Select ProxyServer -ExpandProperty ProxyServer | |
$ProxyEnableV = $ProxyEnable | Select ProxyEnable -ExpandProperty ProxyEnable | |
Write-Verbose " [*] ProxyServer machine settings:" | |
Write-Verbose " [*] - Registry Key : HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Setting\" | |
Write-Verbose " [*] - Registry Property : ProxyServer" | |
Write-Verbose " [*] - Proxy Server : $ProxyServerV" | |
Write-Verbose " [*] - Proxy Enabled : $ProxyEnableV" | |
}else{ | |
Write-Verbose " [*] - ProxyServer machine settings not found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: AutoConfigURL User | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking AutoConfigURL user settings" | |
# Get proxy file | |
$AutoConfigURLU = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "AutoConfigURL" -ErrorAction SilentlyContinue | |
# Get proxy status | |
$AutoProxyUEnable = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -ErrorAction SilentlyContinue | |
# Display object | |
if($AutoConfigURL -ne "") | |
{ | |
$AutoConfigURLV = $AutoConfigURLU | Select AutoConfigURL -ExpandProperty AutoConfigURL | |
$AutoProxyUEnableV = $AutoProxyUEnable | Select ProxyEnable -ExpandProperty ProxyEnable | |
Write-Verbose " [*] AutoConfigURL user setting:" | |
Write-Verbose " [*] - Registry Key : HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Setting\" | |
Write-Verbose " [*] - Registry Property : AutoConfigURL" | |
Write-Verbose " [*] - Proxy Server : $AutoConfigURLV" | |
Write-Verbose " [*] - Proxy Enabled : $AutoProxyUEnableV" | |
}else{ | |
Write-Verbose " [*] - AutoConfigURL user settings not found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: AutoConfigURL Machine | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking AutoConfigURL machine settings" | |
# Get proxy file | |
$AutoConfigURLM = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "AutoConfigURL" -ErrorAction SilentlyContinue | |
# Get proxy status | |
$AutoProxyMEnable = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings" -Name "ProxyEnable" -ErrorAction SilentlyContinue | |
# Display object | |
if($AutoConfigURL -ne "") | |
{ | |
$AutoConfigURLV = $AutoConfigURLM | Select AutoConfigURL -ExpandProperty AutoConfigURL | |
$AutoProxyMEnableV = $AutoProxyMEnable | Select ProxyEnable -ExpandProperty ProxyEnable | |
Write-Verbose " [*] AutoConfigURL machine setting:" | |
Write-Verbose " [*] - Registry Key : HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Setting\" | |
Write-Verbose " [*] - Registry Property : AutoConfigURL" | |
Write-Verbose " [*] - Proxy Server : $AutoConfigURLV" | |
Write-Verbose " [*] - Proxy Enabled : $AutoProxyMEnableV" | |
}else{ | |
Write-Verbose " [*] - AutoConfigURL machine setting not found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: WinHttpSettings User | |
# --------------------------------------------- | |
# Reference: https://github.com/Ichigo49/psh-tools/blob/ee2111986db6bd316e3e90214789d5c9f3837351/lib/Scripts/Proxy.ps1 | |
Write-Verbose "" | |
Write-Verbose " [*] Checking WinHttpSettings user settings" | |
try{ | |
$binval = (Get-ItemProperty "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" -Name WinHttpSettings -ErrorAction SilentlyContinue ).WinHttpSettings | |
$proxylength = $binval[12] | |
if ($proxylength -gt 0) { | |
$proxy = -join ($binval[(12+3+1)..(12+3+1+$proxylength-1)] | % {([char]$_)}) | |
$bypasslength = $binval[(12+3+1+$proxylength)] | |
if ($bypasslength -gt 0) { | |
$bypasslist = -join ($binval[(12+3+1+$proxylength+3+1)..(12+3+1+$proxylength+3+1+$bypasslength)] | % {([char]$_)}) | |
} else { | |
$bypasslist = '(none)' | |
} | |
Write-Verbose " [*] Current WinHTTP proxy user settings:" | |
Write-Verbose " [*] - Proxy Server(s): $proxy" | |
Write-Verbose " [*] - Bypass List : $bypasslist" | |
} else { | |
Write-Verbose " [*] - No WinHttpProxy user settings found." | |
} | |
}catch{ | |
Write-Verbose " [*] - No WinHttpProxy user settings found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: WinHttpSettings Machine | |
# --------------------------------------------- | |
# Reference: https://github.com/Ichigo49/psh-tools/blob/ee2111986db6bd316e3e90214789d5c9f3837351/lib/Scripts/Proxy.ps1 | |
Write-Verbose "" | |
Write-Verbose " [*] Checking WinHttpSettings machine settings" | |
try{ | |
$binval = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections" -Name WinHttpSettings -ErrorAction SilentlyContinue).WinHttpSettings | |
$proxylength = $binval[12] | |
if ($proxylength -gt 0) { | |
$proxy = -join ($binval[(12+3+1)..(12+3+1+$proxylength-1)] | % {([char]$_)}) | |
$bypasslength = $binval[(12+3+1+$proxylength)] | |
if ($bypasslength -gt 0) { | |
$bypasslist = -join ($binval[(12+3+1+$proxylength+3+1)..(12+3+1+$proxylength+3+1+$bypasslength)] | % {([char]$_)}) | |
} else { | |
$bypasslist = '(none)' | |
} | |
Write-Verbose " [*] Current WinHTTP proxy machine settings:" | |
Write-Verbose " [*] - Proxy Server(s): $proxy" | |
Write-Verbose " [*] - Bypass List : $bypasslist" | |
} else { | |
Write-Verbose " [*] - No WinHttpProxy machine settings found." | |
} | |
}catch{ | |
Write-Verbose " [*] - No WinHttpProxy machine settings found." | |
} | |
# --------------------------------------------- | |
# Get Proxy Settings: DefaultConnectionSettings | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking DefaultConnectionSettings settings" | |
#HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections | |
#DefaultConnectionSettings | |
# --------------------------------------------- | |
# Get Proxy Settings: SavedLegacySettings | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking SavedLegacySettings settings" | |
#HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections | |
#SavedLegacySettings | |
# --------------------------------------------- | |
# Get Proxy Settings: ProxyOverride | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking ProxyOverride settings" | |
#HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections | |
#ProxyOverride | |
# --------------------------------------------- | |
# Get Proxy Settings: GP Registry.pol Files | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Checking proxy settings deployed via Group Policy registry files" | |
$ProxyPacFiles = Get-GPRegistryPolicy | where valuedata -like "*.pac*" | select keyname,valuedata,filepath | |
$ProxyPacFileCount = $ProxyPacFiles.count | |
Write-Verbose " [*] Group policy registry.pol files deployed $ProxyPacFileCount .pac proxy files" | |
$ProxyPacFiles | | |
foreach { | |
$KeyName = $_.KeyName | |
$PacFile = $_.ValueData | |
$FilePath = $_.FilePath | |
Write-Verbose " [*] - File Path : $FilePath" | |
Write-Verbose " [*] - Registry Key : $KeyName" | |
Write-Verbose " [*] - Proxy Server : $PacFile" | |
} | |
# --------------------------------------------- | |
# Parse .pac files | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Parsing proxy .pac files" | |
# Create Proxy list | |
$ProxyList = @{} | |
# --------------------------------------------- | |
# Uniq proxy list | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Getting unique proxy list" | |
# --------------------------------------------- | |
# Test access without credentials | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Testing access through proxies without credentials" | |
# A proxy auto-config (PAC) file defines how web browsers and other user agents can automatically choose the appropriate proxy server (access method) for fetching a given URL | |
# --------------------------------------------- | |
# Test access with current/provided credentials | |
# --------------------------------------------- | |
Write-Verbose "" | |
Write-Verbose " [*] Testing access through proxies with credentials" | |
} | |
} | |
# Reference: https://gist.github.com/b4tman/a4ed14e37175dd47acc082abc3846fcf | |
function TestProxy{ | |
[CmdletBinding()] | |
param( | |
[parameter(Mandatory=$true, Position=1)] | |
[string]$ProxyServer="", | |
[parameter(Mandatory=$false, Position=2)] | |
[string]$TestURL="https://www.netspi.com" | |
) | |
try { | |
Invoke-WebRequest -uri $TestURL -Method HEAD -MaximumRedirection 0 -TimeoutSec 14 -UseBasicParsing -UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::InternetExplorer) -DisableKeepAlive -Proxy $ProxyServer -ErrorAction Stop | Out-Null | |
$result = $true | |
} catch [System.InvalidOperationException] { | |
if ($_.FullyQualifiedErrorId -match "MaximumRedirectExceeded") { | |
$result = $true | |
} else { | |
$result = $false | |
} | |
} catch { | |
$result = $false | |
} | |
return $result | |
} | |
# ------------------------------------------------------------- | |
# SUPPORTING FUNCTIONS | |
# ------------------------------------------------------------- | |
#requires -version 5 | |
# ---------------------------------- | |
# Get-GPRegistryPolicy | |
# ---------------------------------- | |
# Parsing functions are based on https://github.com/PowerShell/GPRegistryPolicyParser by Zia Jalali. | |
# Wrapper and Modifications by Scott Sutherland (@_nullbind), NETSPI | |
Function Get-GPRegistryPolicy | |
{ | |
<# | |
.SYNOPSIS | |
This script can be used to find and parse registry.pol files stored on domain controllers. | |
In some cases it can be used to identify cleartext passwords. | |
This is just a wrapper for https://github.com/PowerShell/GPRegistryPolicyParser. | |
By default this will write discovery registries entries to a .csv file. | |
.PARAMETER Domain | |
Set specific domain. | |
.PARAMETER DomainController | |
Set specific domain controller. | |
.Example | |
Get-GPRegistryPolicy -Verbose | |
.Example | |
Get-GPRegistryPolicy -Verbose -Domain domain.com -DomainController 192.168.1.5 | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Set specific domain. This is set to the userdnsdomain by default')] | |
[string]$Domain, | |
[Parameter(Mandatory = $false, | |
HelpMessage = 'Set specific domain controller. This is set to the logon server by default.')] | |
[string]$DomainController | |
) | |
Process | |
{ | |
# Domain Name | |
if(-not $domain){ | |
$domain = $env:USERDNSDOMAIN | |
} | |
# Domain Controller | |
if(-not $DomainController){ | |
$DomainController = $env:LOGONSERVER -replace "\\","" | |
} | |
# Policies Path | |
$ServerPath = "\\$DomainController\sysvol\$domain\Policies" | |
Write-Verbose " [*] - Target Domain: $domain" | |
Write-Verbose " [*] - Target DC : $DomainController" | |
Write-Verbose " [*] - Target Path : $ServerPath" | |
# Check number of policies | |
$Policies = Get-ChildItem \\$DomainController\sysvol\$Domain\Policies | select fullname | |
$PolCount = $Policies.Count | |
Write-Verbose " [*] - $PolCount policy folders found" | |
# Find registry.pol files | |
$RegistryPolFiles = New-Object System.Collections.ArrayList | |
$Policies | | |
Foreach { | |
# Get machine file | |
$PolicyPath = $_.fullname | |
$RegpolPath = "$PolicyPath\Machine\Registry.pol" | |
$RegistryPolFiles += $RegpolPath | |
# Get user file | |
$PolicyPath = $_.fullname | |
$RegpolPath = "$PolicyPath\User\Registry.pol" | |
$RegistryPolFiles += $RegpolPath | |
} | |
# Iterate through discovered files | |
$FileCount = $RegistryPolFiles.Count | |
Write-Verbose " [*] - $FileCount Registry.pol files are being parsed..." | |
$Results = $RegistryPolFiles | | |
Foreach { | |
# Parse discovered file | |
$FullPath = $_ | |
$RegistrySettings = Parse-PolFile -Path "$FullPath" -ErrorAction SilentlyContinue | |
$RegistrySettings | | |
foreach{ | |
# Build custom output object that includes the file path and the domain | |
$object = New-Object psobject | |
$object | add-member noteproperty Domain $Domain | |
$object | add-member noteproperty FilePath $FullPath | |
$object | add-member noteproperty KeyName $_.KeyName | |
$object | add-member noteproperty ValueType $_.ValueType | |
$object | add-member noteproperty ValueLength $_.ValueLength | |
$object | add-member noteproperty ValueData $_.ValueData | |
# Display entry | |
$object | |
} | |
} | |
# Status | |
$EntryCount = $Results.Count | |
Write-Verbose " [*] - $EntryCount registry entries parsed" | |
# Return all registry entries | |
$Results | |
} | |
} | |
########################################################### | |
# | |
# Group Policy - Registry Policy parser module | |
# | |
# Copyright (c) Microsoft Corporation, 2016 | |
# | |
########################################################### | |
# Source: https://github.com/PowerShell/GPRegistryPolicyParser | |
<# | |
------------------------------------------ START OF LICENSE ----------------------------------------- | |
PowerShell-GPRegistryPolicy-Cmdlets v.0.1 | |
Copyright (c) Microsoft Corporation | |
All rights reserved. | |
MIT License | |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
----------------------------------------------- END OF LICENSE ------------------------------------------ | |
#> | |
data LocalizedData | |
{ | |
# culture="en-US" | |
ConvertFrom-StringData @' | |
InvalidHeader = File '{0}' has an invalid header. | |
InvalidVersion = File '{0}' has an invalid version. It should be 1. | |
InvalidFormatBracket = File '{0}' has an invalid format. A [ or ] was expected at location {1}. | |
InvalidFormatSemicolon = File '{0}' has an invalid format. A ; was expected at location {1}. | |
OnlyCreatingKey = Some values are null. Only the registry key is created. | |
InvalidPath = Path {0} doesn't point to an existing registry key/property. | |
InternalError = Internal error while creating a registry entry for {0} | |
InvalidIntegerSize = Invalid size for an integer. Must be less than or equal to 8. | |
'@ | |
} | |
# Import-LocalizedData LocalizedData #-filename GPRegistryPolicyParser.Strings.psd1 | |
$script:REGFILE_SIGNATURE = 0x67655250 # PRef | |
$script:REGISTRY_FILE_VERSION = 0x00000001 #Initially defined as 1, then incremented each time the file format is changed. | |
$script:DefaultEntries = @( | |
"Software\Policies" | |
) | |
Enum RegType { | |
REG_NONE = 0 # No value type | |
REG_SZ = 1 # Unicode null terminated string | |
REG_EXPAND_SZ = 2 # Unicode null terminated string (with environmental variable references) | |
REG_BINARY = 3 # Free form binary | |
REG_DWORD = 4 # 32-bit number | |
REG_DWORD_LITTLE_ENDIAN = 4 # 32-bit number (same as REG_DWORD) | |
REG_DWORD_BIG_ENDIAN = 5 # 32-bit number | |
REG_LINK = 6 # Symbolic link (Unicode) | |
REG_MULTI_SZ = 7 # Multiple Unicode strings, delimited by \0, terminated by \0\0 | |
REG_RESOURCE_LIST = 8 # Resource list in resource map | |
REG_FULL_RESOURCE_DESCRIPTOR = 9 # Resource list in hardware description | |
REG_RESOURCE_REQUIREMENTS_LIST = 10 | |
REG_QWORD = 11 # 64-bit number | |
REG_QWORD_LITTLE_ENDIAN = 11 # 64-bit number (same as REG_QWORD) | |
} | |
Class GPRegistryPolicy | |
{ | |
[string] $KeyName | |
[string] $ValueName | |
[RegType] $ValueType | |
[string] $ValueLength | |
[object] $ValueData | |
GPRegistryPolicy() | |
{ | |
$this.KeyName = $Null | |
$this.ValueName = $Null | |
$this.ValueType = [RegType]::REG_NONE | |
$this.ValueLength = 0 | |
$this.ValueData = $Null | |
} | |
GPRegistryPolicy( | |
[string] $KeyName, | |
[string] $ValueName, | |
[RegType] $ValueType, | |
[string] $ValueLength, | |
[object] $ValueData | |
) | |
{ | |
$this.KeyName = $KeyName | |
$this.ValueName = $ValueName | |
$this.ValueType = $ValueType | |
$this.ValueLength = $ValueLength | |
$this.ValueData = $ValueData | |
} | |
[string] GetRegTypeString() | |
{ | |
[string] $Result = "" | |
switch ($this.ValueType) | |
{ | |
([RegType]::REG_SZ) { $Result = "String" } | |
([RegType]::REG_EXPAND_SZ) { $Result = "ExpandString" } | |
([RegType]::REG_BINARY) { $Result = "Binary" } | |
([RegType]::REG_DWORD) { $Result = "DWord" } | |
([RegType]::REG_MULTI_SZ) { $Result = "MultiString" } | |
([RegType]::REG_QWORD) { $Result = "QWord" } | |
default { $Result = "" } | |
} | |
return $Result | |
} | |
static [RegType] GetRegTypeFromString( [string] $Type ) | |
{ | |
$Result = [RegType]::REG_NONE | |
switch ($Type) | |
{ | |
"String" { $Result = [RegType]::REG_SZ } | |
"ExpandString" { $Result = [RegType]::REG_EXPAND_SZ } | |
"Binary" { $Result = [RegType]::REG_BINARY } | |
"DWord" { $Result = [RegType]::REG_DWORD } | |
"MultiString" { $Result = [RegType]::REG_MULTI_SZ } | |
"QWord" { $Result = [RegType]::REG_QWORD } | |
default { $Result = [RegType]::REG_NONE } | |
} | |
return $Result | |
} | |
} | |
Function New-GPRegistryPolicy | |
{ | |
param ( | |
[Parameter(Mandatory=$true,Position=0)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$keyName, | |
[Parameter(Position=1)] | |
[string] | |
$valueName = $null, | |
[Parameter(Position=2)] | |
[RegType] | |
$valueType = [RegType]::REG_NONE, | |
[Parameter(Position=3)] | |
[string] | |
$valueLength = $null, | |
[Parameter(Position=4)] | |
[object] | |
$valueData = $null | |
) | |
$Policy = [GPRegistryPolicy]::new($keyName, $valueName, $valueType, $valueLength, $valueData) | |
return $Policy; | |
} | |
Function Get-RegType | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$Type | |
) | |
return [GPRegistryPolicy]::GetRegTypeFromString($Type) | |
} | |
<# | |
.SYNOPSIS | |
Reads and parses a .pol file. | |
.DESCRIPTION | |
Reads a .pol file, parses it and returns an array of Group Policy registry settings. | |
.PARAMETER Path | |
Specifies the path to the .pol file. | |
.EXAMPLE | |
C:\PS> Parse-PolFile -Path "C:\Registry.pol" | |
#> | |
Function Parse-PolFile | |
{ | |
[OutputType([Array])] | |
param ( | |
[Parameter(Mandatory=$true,Position=0)] | |
[string] | |
$Path | |
) | |
[Array] $RegistryPolicies = @() | |
$index = 0 | |
[string] $policyContents = Get-Content $Path -Raw | |
[byte[]] $policyContentInBytes = Get-Content $Path -Raw -Encoding Byte | |
# 4 bytes are the signature PReg | |
$signature = [System.Text.Encoding]::ASCII.GetString($policyContents[0..3]) | |
$index += 4 | |
Assert ($signature -eq 'PReg') ($LocalizedData.InvalidHeader -f $Path) | |
# 4 bytes are the version | |
$version = [System.BitConverter]::ToInt32($policyContentInBytes, 4) | |
$index += 4 | |
Assert ($version -eq 1) ($LocalizedData.InvalidVersion -f $Path) | |
# Start processing at byte 8 | |
while($index -lt $policyContents.Length - 2) | |
{ | |
[string]$keyName = $null | |
[string]$valueName = $null | |
[int]$valueType = $null | |
[int]$valueLength = $null | |
[object]$value = $null | |
# Next UNICODE character should be a [ | |
$leftbracket = [System.BitConverter]::ToChar($policyContentInBytes, $index) | |
Assert ($leftbracket -eq '[') "Missing the openning bracket" | |
$index+=2 | |
# Next UNICODE string will continue until the ; less the null terminator | |
$semicolon = $policyContents.IndexOf(";", $index) | |
Assert ($semicolon -ge 0) "Failed to locate the semicolon after key name." | |
$keyName = [System.Text.Encoding]::UNICODE.GetString($policyContents[($index)..($semicolon-3)]) # -3 to exclude the null termination and ';' characters | |
$index = $semicolon + 2 | |
# Next UNICODE string will continue until the ; less the null terminator | |
$semicolon = $policyContents.IndexOf(";", $index) | |
Assert ($semicolon -ge 0) "Failed to locate the semicolon after value name." | |
$valueName = [System.Text.Encoding]::UNICODE.GetString($policyContents[($index)..($semicolon-3)]) # -3 to exclude the null termination and ';' characters | |
$index = $semicolon + 2 | |
# Next DWORD will continue until the ; | |
$semicolon = $index + 4 # DWORD Size | |
Assert ([System.BitConverter]::ToChar($policyContentInBytes, $semicolon) -eq ';') "Failed to locate the semicolon after value type." | |
$valueType = [System.BitConverter]::ToInt32($policyContentInBytes, $index) | |
$index=$semicolon + 2 # Skip ';' | |
# Next DWORD will continue until the ; | |
$semicolon = $index + 4 # DWORD Size | |
Assert ([System.BitConverter]::ToChar($policyContentInBytes, $semicolon) -eq ';') "Failed to locate the semicolon after value length." | |
$valueLength = Convert-StringToInt -ValueString $policyContentInBytes[$index..($index+3)] | |
$index=$semicolon + 2 # Skip ';' | |
if ($valueLength -gt 0) | |
{ | |
# String types less the null terminator for REG_SZ and REG_EXPAND_SZ | |
# REG_SZ: string type (ASCII) | |
if($valueType -eq [RegType]::REG_SZ) | |
{ | |
[string] $value = [System.Text.Encoding]::UNICODE.GetString($policyContents[($index)..($index+$valueLength-3)]) # -3 to exclude the null termination and ']' characters | |
$index += $valueLength | |
} | |
# REG_EXPAND_SZ: string, includes %ENVVAR% (expanded by caller) (ASCII) | |
if($valueType -eq [RegType]::REG_EXPAND_SZ) | |
{ | |
[string] $value = [System.Text.Encoding]::UNICODE.GetString($policyContents[($index)..($index+$valueLength-3)]) # -3 to exclude the null termination and ']' characters | |
$index += $valueLength | |
} | |
# For REG_MULTI_SZ leave the last null terminator | |
# REG_MULTI_SZ: multiple strings, delimited by \0, terminated by \0\0 (ASCII) | |
if($valueType -eq [RegType]::REG_MULTI_SZ) | |
{ | |
[string] $value = [System.Text.Encoding]::UNICODE.GetString($policyContents[($index)..($index+$valueLength-3)]) | |
$index += $valueLength | |
} | |
# REG_BINARY: binary values | |
if($valueType -eq [RegType]::REG_BINARY) | |
{ | |
[byte[]] $value = $policyContentInBytes[($index)..($index+$valueLength-1)] | |
$index += $valueLength | |
} | |
} | |
# DWORD: (4 bytes) in little endian format | |
if($valueType -eq [RegType]::REG_DWORD) | |
{ | |
$value = Convert-StringToInt -ValueString $policyContentInBytes[$index..($index+3)] | |
$index += 4 | |
} | |
# QWORD: (8 bytes) in little endian format | |
if($valueType -eq [RegType]::REG_QWORD) | |
{ | |
$value = Convert-StringToInt -ValueString $policyContentInBytes[$index..($index+7)] | |
$index += 8 | |
} | |
# Next UNICODE character should be a ] | |
$rightbracket = $policyContents.IndexOf("]", $index) # Skip over null data value if one exists | |
Assert ($rightbracket -ge 0) "Missing the closing bracket." | |
$index = $rightbracket + 2 | |
$entry = New-GPRegistryPolicy $keyName $valueName $valueType $valueLength $value | |
$RegistryPolicies += $entry | |
} | |
return $RegistryPolicies | |
} | |
<# | |
.SYNOPSIS | |
Reads registry policies from a list of entries. | |
.DESCRIPTION | |
Reads registry policies from a list of entries and returns an array of GPRegistryPolicies. | |
.PARAMETER Division | |
Specifies the division from which the registry entries will be read. | |
.EXAMPLE | |
C:\PS> Read-RegistryPolicies -Division "LocalMachine" | |
.EXAMPLE | |
C:\PS> Read-RegistryPolicies -Division "LocalMachine" -Entries @('Software\Policies\Microsoft\Windows', 'Software\Policies\Microsoft\WindowsFirewall') | |
#> | |
Function Read-RegistryPolicies | |
{ | |
[OutputType([Array])] | |
param ( | |
[ValidateSet("LocalMachine", "CurrentUser", "Users")] | |
[string] | |
$Division = "LocalMachine", | |
[string[]] | |
$Entries = $script:DefaultEntries | |
) | |
[Array] $RegistryPolicies = @() | |
switch ($Division) | |
{ | |
'LocalMachine' { $Hive = [Microsoft.Win32.Registry]::LocalMachine } | |
'CurrentUser' { $Hive = [Microsoft.Win32.Registry]::CurrentUser } | |
'Users' { $Hive = [Microsoft.Win32.Registry]::Users } | |
} | |
foreach ($entry in $Entries) | |
{ | |
#if (Test-Path -Path $entry) | |
if (IsRegistryKey -Path $entry -Hive $Hive) | |
{ | |
# $entry is a key. | |
$Key = $Hive.OpenSubKey($entry) | |
# Add the key itself | |
$rp = New-GPRegistryPolicy -keyName $entry | |
$RegistryPolicies += $rp | |
# Check default value | |
if ($Key.GetValue('')) | |
{ | |
$info = Get-RegKeyInfo -RegKey $Key -ValueName '' | |
$rp = New-GPRegistryPolicy -keyName $entry -valueName '' -valueType $info.Type -valueLength $info.Size -valueData $info.Data | |
$RegistryPolicies += $rp | |
} | |
if ($Key.ValueCount -gt 0) | |
{ | |
# Copy values under the key | |
$ValueNames = $Key.GetValueNames() | |
foreach($value in $ValueNames) | |
{ | |
if ([System.String]::IsNullOrEmpty($value)) | |
{ | |
$rp = New-GPRegistryPolicy -keyName $entry | |
} | |
else | |
{ | |
$info = Get-RegKeyInfo -RegKey $Key -ValueName $value | |
$rp = New-GPRegistryPolicy -keyName $entry -valueName $value -valueType $info.Type -valueLength $info.Size -valueData $info.Data | |
} | |
$RegistryPolicies += $rp | |
} | |
} | |
if ($Key.SubKeyCount -gt 0) | |
{ | |
# Copy subkeys recursively | |
$SubKeyNames = $Key.GetSubKeyNames() | |
$newEntries = @() | |
foreach($subkey in $SubKeyNames) | |
{ | |
$newEntry = Join-Path -Path $entry -ChildPath $subkey | |
$newEntries += ,$newEntry | |
} | |
$RegistryPolicies += Read-RegistryPolicies -Entries $newEntries -Division $Division | |
} | |
} | |
else | |
{ | |
$Tokens = $entry.Split('\') | |
$Property = $Tokens[-1] | |
$ParentKey = $Tokens[0..($Tokens.Count-2)] -join '\' | |
$NoSuchKeyOrProperty = $false | |
if (IsRegistryKey -Path $ParentKey -Hive $Hive) | |
{ | |
# $entry is a property. | |
# [key;value;type;size;data] | |
$Key = $Hive.OpenSubKey($ParentKey) | |
if ($Key.GetValueNames() -icontains $Property) | |
{ | |
$info = Get-RegKeyInfo -RegKey $Key -ValueName $Property | |
$rp = [GPRegistryPolicy]::new($ParentKey, $Property, $info.Type, $info.Size, $info.Data) | |
$RegistryPolicies += $rp | |
} | |
else | |
{ | |
$NoSuchKeyOrProperty = $true | |
} | |
} | |
else | |
{ | |
$NoSuchKeyOrProperty = $true | |
} | |
if ( $NoSuchKeyOrProperty -and @('Continue', 'SilentlyContinue', 'Ignore' ) -inotcontains $ErrorActionPreference) | |
{ | |
# $entry points to a key/property that doesn't exist. | |
$NoSuchKeyOrProperty = $true | |
Fail -ErrorMessage ($LocalizedData.InvalidPath -f $entry) | |
} | |
} | |
} | |
return $RegistryPolicies | |
} | |
<# | |
.SYNOPSIS | |
Creates a .pol file entry byte array from a GPRegistryPolicy instance. | |
.DESCRIPTION | |
Creates a .pol file entry byte array from a GPRegistryPolicy instance. This entry can be written | |
in a .pol file later. | |
.PARAMETER RegistryPolicy | |
Specifies the registry policy entry. | |
#> | |
Function Create-RegistrySettingsEntry | |
{ | |
[OutputType([Array])] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[alias("RP")] | |
[GPRegistryPolicy] | |
$RegistryPolicy | |
) | |
# Entry format: [key;value;type;size;data] | |
[Byte[]] $Entry = @() | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes('[') # Openning bracket | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes($RP.KeyName + "`0") | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes(';') # semicolon as delimiter | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes($RP.ValueName + "`0") | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes(';') # semicolon as delimiter | |
$Entry += [System.BitConverter]::GetBytes([Int32]$RP.ValueType) | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes(';') # semicolon as delimiter | |
#Assert $type ($LocalizedData.InternalError -f $key) | |
# Get data bytes then compute byte size based on data and type | |
switch ($RP.ValueType) | |
{ | |
{ @([RegType]::REG_SZ, [RegType]::REG_EXPAND_SZ, [RegType]::REG_MULTI_SZ) -contains $_ } | |
{ | |
$dataBytes = [System.Text.Encoding]::Unicode.GetBytes($RP.ValueData + "`0") | |
$dataSize = $dataBytes.Count | |
} | |
([RegType]::REG_BINARY) | |
{ | |
$dataBytes = [System.Text.Encoding]::Unicode.GetBytes($RP.ValueData) | |
$dataSize = $dataBytes.Count | |
} | |
([RegType]::REG_DWORD) | |
{ | |
$dataBytes = [System.BitConverter]::GetBytes([Int32]$RP.ValueData) | |
$dataSize = 4 | |
} | |
([RegType]::REG_QWORD) | |
{ | |
$dataBytes = [System.BitConverter]::GetBytes([Int64]$RP.ValueData) | |
$dataSize = 8 | |
} | |
default | |
{ | |
$dataBytes = [System.Text.Encoding]::Unicode.GetBytes("") | |
$dataSize = 0 | |
} | |
} | |
#Assert $type ($LocalizedData.InternalError -f $key) | |
$Entry += [System.BitConverter]::GetBytes($dataSize) | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes(';') # semicolon as delimiter | |
#Assert $type ($LocalizedData.InternalError -f $key) | |
$Entry += $dataBytes | |
$Entry += [System.Text.Encoding]::Unicode.GetBytes(']') # Closing bracket | |
return $Entry | |
} | |
<# | |
.SYNOPSIS | |
Appends an array of registry policy entries to a file. | |
.DESCRIPTION | |
Appends an array of registry policy entries to a file. | |
.PARAMETER RegistryPolicies | |
An array of registry policy entries. | |
.PARAMETER Path | |
Path to a file (.pol extension) | |
#> | |
Function Append-RegistryPolicies | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[GPRegistryPolicy[]] | |
$RegistryPolicies, | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$Path | |
) | |
foreach ($rp in $RegistryPolicies) | |
{ | |
[Byte[]] $Entry = Create-RegistrySettingsEntry -RegistryPolicy $rp | |
$Entry | Add-Content -Path $Path -Encoding Byte | |
} | |
} | |
Function Assert | |
{ | |
param ( | |
[Parameter(Mandatory)] | |
$Condition, | |
[Parameter(Mandatory)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$ErrorMessage | |
) | |
if (!$Condition) | |
{ | |
Fail -ErrorMessage $ErrorMessage; | |
} | |
} | |
Function Fail | |
{ | |
param ( | |
[Parameter(Mandatory)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$ErrorMessage | |
) | |
throw $ErrorMessage | |
} | |
<# | |
.SYNOPSIS | |
Creates a file and initializes it with Group Policy Registry file format signature. | |
.DESCRIPTION | |
Creates a file and initializes it with Group Policy Registry file format signature. | |
.PARAMETER Path | |
Path to a file (.pol extension) | |
#> | |
Function Create-GPRegistryPolicyFile | |
{ | |
param ( | |
[Parameter(Mandatory)] | |
$Path | |
) | |
$null = Remove-Item -Path $Path -Force -Verbose -ErrorAction SilentlyContinue | |
New-Item -Path $Path -Force -Verbose -ErrorAction Stop | Out-Null | |
[System.BitConverter]::GetBytes($script:REGFILE_SIGNATURE) | Add-Content -Path $Path -Encoding Byte | |
[System.BitConverter]::GetBytes($script:REGISTRY_FILE_VERSION) | Add-Content -Path $Path -Encoding Byte | |
} | |
<# | |
.SYNOPSIS | |
Returns the type, size and data values of a given registry key. | |
.DESCRIPTION | |
Returns the type, size and data values of a given registry key. | |
.PARAMETER RegKey | |
Registry Key | |
.PARAMETER ValueName | |
The name of the Value under the given registry key | |
#> | |
Function Get-RegKeyInfo | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[Microsoft.Win32.RegistryKey] | |
$RegKey, | |
[Parameter(Mandatory = $true)] | |
[AllowEmptyString()] | |
[string] | |
$ValueName | |
) | |
switch ($RegKey.GetValueKind($ValueName)) | |
{ | |
"String" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$Data = $RegKey.GetValue($ValueName) | |
$Size = $Data.Length | |
} | |
"ExpandString" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$Data = $RegKey.GetValue($ValueName,$null,[Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) | |
$Size = $Data.Length | |
} | |
"Binary" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$value = $RegKey.GetValue($ValueName) | |
$Data = [System.Text.Encoding]::Unicode.GetString($value) | |
$Size = $Data.Count | |
} | |
"DWord" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$Data = $RegKey.GetValue($ValueName) | |
$Size = 4 | |
} | |
"MultiString" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$Data = ($RegKey.GetValue($ValueName) -join "`0") + "`0" | |
$Size = $Data.Length | |
} | |
"QWord" { | |
$Type = $RegKey.GetValueKind($ValueName) | |
$Data = $RegKey.GetValue($ValueName) | |
$Size = 8 | |
} | |
default { | |
$Type = $null | |
$Data = $null | |
$Size = 0 | |
} | |
} | |
return @{ | |
'Type' = $Type; | |
'Size' = $Size; | |
'Data' = $Data; | |
} | |
} | |
Function IsRegistryKey | |
{ | |
param ( | |
[Parameter(Mandatory = $true)] | |
[ValidateNotNullOrEmpty()] | |
[string] | |
$Path, | |
[Microsoft.Win32.RegistryKey] | |
$Hive = [Microsoft.Win32.Registry]::LocalMachine | |
) | |
$key = $Hive.OpenSubKey($Path) | |
if ($key) | |
{ | |
if ($PSVersionTable.PSEdition -ieq 'Core') | |
{ | |
$key.Flush() | |
$key.Dispose() | |
} | |
else | |
{ | |
$key.Close() | |
} | |
return $true | |
} | |
else | |
{ | |
return $false | |
} | |
} | |
Function Convert-StringToInt | |
{ | |
param ( | |
[Parameter(Mandatory)] | |
[ValidateNotNullOrEmpty()] | |
[System.Object[]] | |
$ValueString | |
) | |
if ($ValueString.Length -le 4) | |
{ | |
[int32] $result = 0 | |
} | |
elseif ($ValueString.Length -le 8) | |
{ | |
[int64] $result = 0 | |
} | |
else | |
{ | |
Fail -ErrorMessage $LocalizedData.InvalidIntegerSize | |
} | |
for ($i = $ValueString.Length - 1 ; $i -ge 0 ; $i -= 1) | |
{ | |
$result = $result -shl 8 | |
$result = $result + ([int][char]$ValueString[$i]) | |
} | |
return $result | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Line 175 & 208 seems like a typo, WinHttPSettings instead of WinHttpSettings