<# BSD 3-Clause License Copyright (c) 2023, FB Pro GmbH All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #> #region Import tests configuration settings $CisBenchmarks = @{ FirefoxLockPrefSettings = @( @{ Id = "2.1" Task = "Enable Automatic Updates" LockPrefs = @( @{ Name = "app.update.auto"; Value = $true } @{ Name = "app.update.enabled"; Value = $true } @{ Name = "app.update.staging.enabled"; Value = $true } ) } @{ Id = "2.2" Task = "Enable Auto-Notification of Outdated Plugins" LockPrefs = @( @{ Name = "plugins.update.notifyUser"; Value = $true } ) } @{ Id = "2.3" Task = "Enable Information Bar for Outdated Plugins" LockPrefs = @( @{ Name = "plugins.hide_infobar_for_outdated_plugin"; Value = $false } ) } @{ Id = "2.4" Task = "Set Update Interval Time Checks" LockPrefs = @( @{ Name = "app.update.interval"; Value = 43200 } ) } @{ Id = "2.5" Task = "Set Update Wait Time Prompt" LockPrefs = @( @{ Name = "app.update.promptWaitTime"; Value = 172800 } ) } @{ Id = "2.6" Task = "Ensure Update-related UI Components are Displayed" LockPrefs = @( @{ Name = "app.update.silent"; Value = $false } ) } @{ Id = "2.7" Task = "Set Search Provider Update Behavior" LockPrefs = @( @{ Name = "app.update.auto"; Value = $true } @{ Name = "app.update.enabled"; Value = $true } ) } # @{ # Id = "3.1" # Task = "Validate Proxy Settings" # } @{ Id = "3.2" Task = "Do Not Send Cross SSLTLS Referrer Header" LockPrefs = @( @{ Name = "network.http.sendSecureXSiteReferrer"; Value = $false } ) } @{ Id = "3.3" Task = "Disable NTLM v1" LockPrefs = @( @{ Name = "network.auth.force-generic-ntlm-v1"; Value = $false } ) } @{ Id = "3.4" Task = "Enable Warning For Phishy URLs" LockPrefs = @( @{ Name = "network.http.phishy-userpass-length"; Value = 1 } ) } @{ Id = "3.5" Task = "Enable IDN Show Punycode" LockPrefs = @( @{ Name = "network.IDN_show_punycode"; Value = $true } ) } @{ Id = "3.6" Task = "Set File URI Origin Policy" LockPrefs = @( @{ Name = "security.fileuri.strict_origin_policy"; Value = $true } ) } @{ Id = "3.7" Task = "Disable Cloud Sync" LockPrefs = @( @{ Name = "services.sync.enabled"; Value = $false } ) } @{ Id = "3.8" Task = "Disable WebRTC" LockPrefs = @( @{ Name = "media.peerconnection.enabled"; Value = $false } @{ Name = "media.peerconnection.use_document_iceservers"; Value = $false } ) } @{ Id = "4.1" Task = "Set SSL Override Behavior" LockPrefs = @( @{ Name = "browser.ssl_override_behavior"; Value = 0 } ) } @{ Id = "4.2" Task = "Set Security TLS Version Maximum" LockPrefs = @( @{ Name = "security.tls.version.max"; Value = 3 } ) } @{ Id = "4.3" Task = "Set Security TLS Version Minimum " LockPrefs = @( @{ Name = "security.tls.version.min"; Value = 1 } ) } @{ Id = "4.4" Task = "Set OCSP Use Policy" LockPrefs = @( @{ Name = "security.OCSP.enabled"; Value = 1 } ) } @{ Id = "4.5" Task = "Block Mixed Active Content" LockPrefs = @( @{ Name = "security.mixed_content.block_active_content"; Value = $true } ) } @{ Id = "4.6" Task = "Set OCSP Response Policy" LockPrefs = @( @{ Name = "security.OCSP.require"; Value = $true } ) } @{ Id = "5.1" Task = "Disallow JavaScripts Ability to Change the Status Bar Text" LockPrefs = @( @{ Name = "dom.disable_window_status_change"; Value = $true } ) } @{ Id = "5.2" Task = "Disable Scripting of Plugins by JavaScript" LockPrefs = @( @{ Name = "security.xpconnect.plugin.unrestricted"; Value = $false } ) } @{ Id = "5.3" Task = "Disallow JavaScripts Ability to Hide the Address Bar" LockPrefs = @( @{ Name = "dom.disable_window_open_feature.location"; Value = $true } ) } @{ Id = "5.4" Task = "Disallow JavaScripts Ability to Hide the Status Bar" LockPrefs = @( @{ Name = "dom.disable_window_open_feature.status"; Value = $true } ) } @{ Id = "5.5" Task = "Disable Closing of Windows via Scripts" LockPrefs = @( @{ Name = "dom.allow_scripts_to_close_windows"; Value = $false } ) } @{ Id = "5.6" Task = "Block Pop-up Windows" LockPrefs = @( @{ Name = "privacy.popups.policy"; Value = 1 } ) } @{ Id = "5.7" Task = "Disable Displaying JavaScript in History URLs" LockPrefs = @( @{ Name = "browser.urlbar.filter.javascript"; Value = $true } ) } @{ Id = "6.1" Task = "Disallow Credential Storage" LockPrefs = @( @{ Name = "signon.rememberSignons"; Value = $false } ) } @{ Id = "6.2" Task = "Do Not Accept Third Party Cookies" LockPrefs = @( @{ Name = "network.cookie.cookieBehavior"; Value = 1 } ) } @{ Id = "6.3" Task = "Tracking Protection" LockPrefs = @( @{ Name = "privacy.donottrackheader.enabled"; Value = $true } @{ Name = "privacy.donottrackheader.value"; Value = 1 } @{ Name = "privacy.trackingprotection.enabled"; Value = $true } @{ Name = "privacy.trackingprotection.pbmode"; Value = $true } ) } @{ Id = "6.4" Task = "Set Delay for Enabling Security Sensitive Dialog Boxes" LockPrefs = @( @{ Name = "security.dialog_enable_delay"; Value = 2000 } ) } @{ Id = "6.5" Task = "Disable Geolocation Serivces" LockPrefs = @( @{ Name = "geo.enabled"; Value = $false } ) } @{ Id = "7.1" Task = "Secure Application Plug-ins" LockPrefs = @( @{ Name = "browser.helperApps.alwaysAsk.force"; Value = $true } ) } @{ Id = "7.2" Task = "Disabling Auto-Install of Add-ons" LockPrefs = @( @{ Name = "xpinstall.whitelist.required"; Value = $true } ) } @{ Id = "7.3" Task = "Enable Extension Block List" LockPrefs = @( @{ Name = "extensions.blocklist.enabled"; Value = $true } ) } @{ Id = "7.4" Task = "Set Extension Block List Interval" LockPrefs = @( @{ Name = "extensions.blocklist.interval"; Value = 86400 } ) } @{ Id = "7.5" Task = "Enable Warning for External Protocol Handler" LockPrefs = @( @{ Name = "network.protocol-handler.warn-external-default"; Value = $true } ) } @{ Id = "7.6" Task = "Disable Popups Initiated by Plugins" LockPrefs = @( @{ Name = "privacy.popups.disable_from_plugins"; Value = 2 } ) } @{ Id = "7.7" Task = "Enable Extension Auto Update" LockPrefs = @( @{ Name = "extensions.update.autoUpdateDefault"; Value = $true } ) } @{ Id = "7.8" Task = "Enable Extension Update" LockPrefs = @( @{ Name = "extensions.update.enabled"; Value = $true } ) } @{ Id = "7.9" Task = "Set Extension Update Interval Time Checks" LockPrefs = @( @{ Name = "extensions.update.interval"; Value = 86400 } ) } @{ Id = "8.1" Task = "Enable Virus Scanning for Downloads" LockPrefs = @( @{ Name = "browser.download.manager.scanWhenDone"; Value = $true } ) } @{ Id = "8.2" Task = "Disable JAR from Opening Unsafe File Types" LockPrefs = @( @{ Name = "network.jar.open-unsafe-types"; Value = $false } ) } @{ Id = "8.3" Task = "Block Reported Web Forgeries" LockPrefs = @( @{ Name = "browser.safebrowsing.enabled"; Value = $true } ) } @{ Id = "8.4" Task = "Block Reported Attack Sites" LockPrefs = @( @{ Name = "browser.safebrowsing.malware.enabled"; Value = $true } ) } ) } $DisaRequirements = @{ # RegistrySettings = @( # @{ # Id = "DTBF003" # Task = "Installed version of Firefox unsupported." # Path = "HKLM\Software\Mozilla\Mozilla Firefox\CurrentVersion" # Name = "firefox.exe" # Value = 0 # is equal to or greater than 50.1.x (or ESR 45.7.x) # } # ) FirefoxLockPrefSettings = @( @{ Id = "DTBF030" Task = "Firewall traversal from remote host must be disabled." LockPrefs = @( @{ Name = "security.enable_tls"; Value = $true } @{ Name = "security.tls.version.min"; Value = 2 } @{ Name = "security.tls.version.max"; Value = 3 } ) } @{ Id = "DTBF050" Task = "FireFox is configured to ask which certificate to present to a web site when a certificate is required." LockPrefs = @( @{ Name = "security.default_personal_cert"; Value = "Ask Every Time" } ) } # @{ # Not set - in CIS Benchmarks # Id = "DTBF080" # Task = "Firefox application is set to auto-update." # } @{ Id = "DTBF085" Task = "Firefox automatically checks for updated version of installed Search plugins." LockPrefs = @( @{ Name = "browser.search.update"; Value = $false } ) } @{ Id = "DTBF090" Task = "Firefox automatically updates installed add-ons and plugins." LockPrefs = @( @{ Name = "extensions.update.enabled"; Value = $false } ) } @{ Id = "DTBF105" Task = "Network shell protocol is enabled in FireFox." LockPrefs = @( @{ Name = "network.protocol-handler.external.shell"; Value = $false } ) } # @{ # no longer available # Id = "DTBF110" # Task = "Firefox is not configured to prompt a user before downloading and opening required file types." # } # @{ # no longer available # Id = "DTBF130" # Task = "Firefox is not configured to provide warnings when a user switches from a secure (SSL-enabled) to a non-secure page." # } @{ Id = "DTBF140" Task = "Firefox formfill assistance option is disabled." LockPrefs = @( @{ Name = "browser.formfill.enable"; Value = $false } ) } @{ Id = "DTBF150" Task = "Firefox is configured to autofill passwords." LockPrefs = @( @{ Name = "signon.autofillForms"; Value = $false } ) } # @{ # Not set - in CIS Benchmarks # Id = "DTBF160" # Task = "FireFox is configured to use a password store with or without a master password." # } # @{ # Not set - see CIS benchmark 5.4_L1_Disallow_JavaScripts_Ability_to_Hide_the_Status_Bar # Id = "DTBF180" # Task = "FireFox is not configured to block pop-up windows. # } @{ Id = "DTBF181" Task = "FireFox is configured to allow JavaScript to move or resize windows." LockPrefs = @( @{ Name = "dom.disable_window_move_resize"; Value = $true } ) } @{ Id = "DTBF183" Task = " Firefox is configured to allow JavaScript to disable or replace context menus." LockPrefs = @( @{ Name = "dom.event.contextmenu.enabled"; Value = $false } ) } # @{ # Not set - in CIS Benchmarks # Id = "DTBF184" # Task = "Firefox is configured to allow JavaScript to hide or change the status bar." # } # @{ # no longer available # Id = "DTBF186" # Task = "Extensions install must be disabled." # } @{ Id = "DTBF190" Task = "Background submission of information to Mozilla must be disabled." LockPrefs = @( @{ Name = "datareporting.policy.dataSubmissionEnabled"; Value = $false } @{ Name = "datareporting.healthreport.service.enabled"; Value = $false } @{ Name = "datareporting.healthreport.uploadEnabled"; Value = $false } ) } ) } #endregion #region helper classes class LockPrefSetting { [string] $Name $Value } #endregion #region Helper functions function Get-FirefoxInstallDirectory { if (Test-Path 'HKLM:\SOFTWARE\WOW6432Node\Mozilla\Mozilla Firefox\') { $firefoxPath = 'HKLM:\SOFTWARE\WOW6432Node\Mozilla\Mozilla Firefox\' }if (Test-Path 'HKLM:\SOFTWARE\Mozilla\Mozilla Firefox\') { $firefoxPath = 'HKLM:\SOFTWARE\Mozilla\Mozilla Firefox\' } if(-not($null -eq $firefoxPath)){ $currentFirefox = Get-ChildItem -Path $firefoxPath | Select-Object -Last 1 $installDir = $currentFirefox | Get-ChildItem | Where-Object PSChildName -EQ "Main" return $installDir | Get-ItemProperty | Select-Object -ExpandProperty "Install Directory" } else{ Write-Output "Mozilla Firefox is not installed on OS" } # $firefoxPath = "HKLM:\SOFTWARE\WOW6432Node\Mozilla\Mozilla Firefox\" # if (-not (Test-Path $firefoxPath)) { # $firefoxPath = "HKLM:\SOFTWARE\Mozilla\Mozilla Firefox\" # } } function Get-FirefoxLocalSettingsFile { return "{0}\defaults\pref\local-settings.js" -f (Get-FirefoxInstallDirectory) } function Get-FirefoxMozillaCfgFileName { $localSettingsFilePath = Get-FirefoxLocalSettingsFile $content = if (Test-Path $localSettingsFilePath) { Get-Content $localSettingsFilePath } else { $null } $filename = $content | ForEach-Object { if ($_ -match "^pref\(`"general\.config\.filename`",\s?`"([\w\-. ]+\.cfg)`"\);") { return $Matches[1] } return $null } | Where-Object { $null -ne $_ } | Select-Object -Last 1 if ($null -eq $filename) { return "mozilla.cfg" } return $filename } function Get-FirefoxMozillaCfgFile { return "{0}\{1}" -f (Get-FirefoxInstallDirectory), (Get-FirefoxMozillaCfgFileName) } function Get-FirefoxLockPrefs { if (-not (Test-Path (Get-FirefoxMozillaCfgFile))) { return $null } $regex = "^lockPref\s*\(\s*`"([\w.-]+)`"\s*,\s*({0}|{1}|{2})\s*\);" -f @( "(?true|false)" "(?\d+)" "`"(?(\\.|[^`"\\])*)`"" ) $currentLockPrefs = Get-Content (Get-FirefoxMozillaCfgFile) | ForEach-Object { if ($_ -match $regex) { $value = $null if ($Matches.Keys -contains "bool") { $value = [bool]::Parse($Matches["bool"]) } elseif ($Matches.Keys -contains "number") { $value = [int]::Parse($Matches["number"]) } elseif ($Matches.Keys -contains "string") { $value = $Matches["string"] } [LockPrefSetting]@{ Name = $Matches[1]; Value = $value } } } | Where-Object { $null -ne $_ } return $currentLockPrefs } #endregion #region Audit functions function Get-RegistryAudit { [CmdletBinding()] Param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Id, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Task, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Path, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Name, [Parameter(ValueFromPipelineByPropertyName = $true)] [AllowEmptyString()] [object[]] $Value, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [ScriptBlock] $Predicate, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [String] $ExpectedValue, [Parameter(ValueFromPipelineByPropertyName = $true)] [bool] $DoesNotExist = $false ) process { try { $regValues = Get-ItemProperty -ErrorAction Stop -Path $Path -Name $Name ` | Select-Object -ExpandProperty $Name if (-not (& $Predicate $regValues)) { $regValue = $regValues -join ", " return @{ Id = $Id Task = $Task Message = "Registry value: $regValue. Differs from allowed value: $ExpectedValue." Status = "False" } } } catch [System.Management.Automation.PSArgumentException] { if ($DoesNotExist) { return @{ Id = $Id Task = $Task Message = "Compliant. Registry value not set." Status = "True" } } return @{ Id = $Id Task = $Task Message = "Registry value not found." Status = "False" } } catch [System.Management.Automation.ItemNotFoundException] { if ($DoesNotExist) { return @{ Id = $Id Task = $Task Message = "Compliant. Registry value not set." Status = "True" } } return @{ Id = $Id Task = $Task Message = "Registry key not found." Status = "False" } } return @{ Id = $Id Task = $Task Message = "Compliant" Status = "True" } } } function Get-FirefoxLocalSettingsFileAudit { $Id = "1.1" $Task = "Create local-settings.js file" if (-not (Test-Path (Get-FirefoxLocalSettingsFile))){ return @{ Id = $Id Task = $Task Message = "local-settings.js file does not exist." Status = "False" } } $generalConfigFilename = Get-Content (Get-FirefoxLocalSettingsFile) | Where-Object { $_ -match "^pref\s*\(\s*`"general\.config\.filename`"\s*,\s*`"([\w\-. ]+\.cfg)`"\s*\);" } if ($generalConfigFilename.Count -eq 0) { return @{ Id = $Id Task = $Task Message = "File does not set 'general.config.filename'" Status = "False" } } $generalConfigObscure = Get-Content (Get-FirefoxLocalSettingsFile) | Where-Object { $_ -match "^pref\s*\(\s*`"general\.config\.obscure_value`"\s*,\s*0\s*\);" } if ($generalConfigObscure.Count -eq 0) { return @{ Id = $Id Task = $Task Message = "File does not set 'general.config.obscure' = 0" Status = "False" } } return @{ Id = $Id Task = $Task Message = "Compliant" Status = "True" } } function Get-FirefoxMozillaCfgFileAudit { $name = Get-FirefoxMozillaCfgFileName $Id = "1.3" $Task = "Create $name file" if (-not (Test-Path (Get-FirefoxMozillaCfgFile))){ return @{ Id = $Id Task = $Task Message = "$name file does not exist." Status = "False" } } return @{ Id = $Id Task = $Task Message = "Compliant" Status = "True" } } function Get-FileAudit { [CmdletBinding()] Param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Id, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Task, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Path, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [scriptblock] $Predicate ) process { if (-not (Test-Path $Path)) { return @{ Id = $Id Task = $Task Message = "File does not exist." Status = "False" } } if (-not (&$Predicate (Get-Content $Path))) { return @{ Id = $Id Task = $Task Message = "File does not match predicate." Status = "False" } } return @{ Id = $Id Task = $Task Message = "Compliant." Status = "True" } } } function Get-LockPrefSettingAudit { [CmdletBinding()] Param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Id, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [string] $Task, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [array] $LockPrefs, [LockPrefSetting[]] $CurrentLockPrefs = (Get-FirefoxLockPrefs) ) process { if ($null -eq $CurrentLockPrefs) { return @{ Id = $Id Task = $Task Message = "general config does not exist." Status = "None" } } $missingLockPrefs = $LockPrefs | Where-Object { $LockPref = $_ # LockPref not in currentLockPrefs ($currentLockPrefs | Where-Object { ($_.Name -eq $LockPref.Name) -and ($_.Value -is $LockPref.Value.GetType()) -and ($_.Value -eq $LockPref.Value) }).Count -eq 0 } if ($missingLockPrefs.Count -gt 0) { $msg = ($missingLockPrefs | ForEach-Object { "lockPref(`"{0}`", {1})" -f $_.Name, $_.Value }) -join "; " return @{ Id = $Id Task = $Task Message = "Missing lockprefs: $msg." Status = "False" } } return @{ Id = $Id Task = $Task Message = "Compliant." Status = "True" } } } #endregion $currentLockPrefs = Get-FirefoxLockPrefs [Report] @{ Title = 'Mozilla Firefox Audit Report' ModuleName = 'ATAPAuditor' BasedOn = @( 'CIS Mozilla Firefox 38 ESR Benchmark, Version: 1.0.0, Date: 2015-12-31' 'DISA Mozilla FireFox Security Technical Implementation Guide, Version: V4R24, Date: 2019-01-25' ) Sections = @( [ReportSection] @{ Title = 'CIS Benchmarks' Description = 'This section contains all CIS benchmarks' Subsections = @( [ReportSection] @{ Title = "Configure Locked Preferences" AuditInfos = @( Get-FirefoxLocalSettingsFileAudit # missing 1.2 Get-FirefoxMozillaCfgFileAudit # missing 1.4 # missing 1.5 ) } [ReportSection] @{ Title = "Preference Settings" AuditInfos = foreach ($setting in $CisBenchmarks.FirefoxLockPrefSettings) { $obj = New-Object -TypeName psobject -Property $setting Write-Output ($obj | Get-LockPrefSettingAudit -CurrentLockPrefs $currentLockPrefs) } } ) } [ReportSection] @{ Title = 'DISA Recommendations' Description = 'This section contains all DISA recommendations' Subsections = @( [ReportSection] @{ Title = "Preference Settings" AuditInfos = foreach ($setting in $DisaRequirements.FirefoxLockPrefSettings) { $obj = New-Object -TypeName psobject -Property $setting Write-Output ($obj | Get-LockPrefSettingAudit -CurrentLockPrefs $currentLockPrefs) } } ) } ) }