functions/System/Drift/Get-NetworkSecurityDrift.ps1
|
function Get-NetworkSecurityDrift { <# .SYNOPSIS Detects configuration drift in network security settings across profiles. .DESCRIPTION Comprehensive network security drift detection supporting multiple security profiles (Basis, Recommended, Strict). Checks SMB, NTLM, LDAP, Kerberos, encryption, and protocol security settings. Supports both local and remote computer analysis with optional detailed output. Returns PSCustomObject array with drift findings. .PARAMETER ComputerName Target computer name for drift analysis (default: 'localhost' for local computer). .PARAMETER Profile Security baseline profile: Basis (minimum), Recommended (default), or Strict (maximum hardening). Different profiles enforce different security levels for network protocols. .PARAMETER Detailed Include additional detailed checks (network adapters, TLS protocols, IPsec). Automatically enabled for Recommended+ profiles in most contexts. .PARAMETER ReportDriftOnly Return only items with DRIFT status, filtering out COMPLIANT items. Useful for compliance reports focusing on deviations. .PARAMETER Credential PowerShell credential object for authenticating remote computer access. Only used when ComputerName is not 'localhost'. .PARAMETER NTLMv2Level Expected NTLM compatibility level override (0-5, default: 5 = NTLMv2 Only). Allows customization of NTLM enforcement expectations. .EXAMPLE $drifts = Get-NetworkSecurityDrift -Profile Recommended $drifts | Format-Table -AutoSize .EXAMPLE Get-NetworkSecurityDrift -ComputerName SERVER01 -Profile Strict -Credential $cred -Detailed .EXAMPLE Get-NetworkSecurityDrift -Profile Basis -ReportDriftOnly | Export-Csv -Path drifts.csv .NOTES DEPENDS ON: Write-Log (Core) APPLIES TO: Windows Server 2016+, Windows 10+ PROFILES: Basis (11 checks), Recommended (8+ checks), Strict (10+ checks with detailed) #> [CmdletBinding(SupportsShouldProcess)] param( [string]$ComputerName = 'localhost', [ValidateSet('Basis', 'Recommended', 'Strict')] [string]$Profile = 'Recommended', [switch]$Detailed, [switch]$ReportDriftOnly, [pscredential]$Credential, [ValidateRange(0, 5)] [int]$NTLMv2Level = 5 ) $findings = @() try { if (-not $PSCmdlet.ShouldProcess($ComputerName, "Check Network Security Drift")) { return $findings } # Build remote execution parameters $remoteParams = @{ ErrorAction = 'SilentlyContinue' } if ($ComputerName -ne 'localhost') { $remoteParams['ComputerName'] = $ComputerName if ($Credential) { $remoteParams['Credential'] = $Credential } } # Profile-based configuration switch ($Profile) { 'Basis' { $expectedSMB1 = 'Disabled' $expectedNTLMMin = 3 $expectedNTLMMax = 5 $checkSMBSigning = $false $checkLDAP = $false $checkLLMNR = $false $checkKerberos = $false $checkSMBEncrypt = $false $checkTLS = $false $checkIPsec = $false } 'Recommended' { $expectedSMB1 = 'Disabled' $expectedNTLMMin = 5 $expectedNTLMMax = 5 $checkSMBSigning = $true $checkLDAP = $true $checkLLMNR = $true $checkKerberos = $false $checkSMBEncrypt = $false $checkTLS = $Detailed $checkIPsec = $false } 'Strict' { $expectedSMB1 = 'Disabled' $expectedNTLMMin = 5 $expectedNTLMMax = 5 $checkSMBSigning = $true $checkLDAP = $true $checkLLMNR = $true $checkKerberos = $true $checkSMBEncrypt = $true $checkTLS = $Detailed $checkIPsec = $Detailed } } # [CHECK 1] SMB1 Protocol (All profiles) $smb1State = 'Unknown' try { $smb1Feature = Get-WindowsOptionalFeature -FeatureName SMB1Protocol -Online @remoteParams if ($smb1Feature) { $smb1State = $smb1Feature.State } } catch { Write-Log -Message "Warning: Unable to query SMB1 feature status: $_" -Level Warning -Caller $MyInvocation.MyCommand.Name $smb1State = 'Disabled' } if ($smb1State -eq 'Enabled') { $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'SMB1 Protocol' Expected = $expectedSMB1 Actual = 'Enabled' Status = 'DRIFT' Severity = 'CRITICAL' ComputerName = $ComputerName } } else { if ($smb1State -eq 'Unknown') { $smb1Actual = 'Unknown (unable to query)' $smb1Status = 'UNKNOWN' } else { $smb1Actual = 'Disabled' $smb1Status = 'COMPLIANT' } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'SMB1 Protocol' Expected = $expectedSMB1 Actual = $smb1Actual Status = $smb1Status Severity = 'INFO' ComputerName = $ComputerName } } # [CHECK 2] SMB2/3 Signing Enforcement (Recommended+) if ($checkSMBSigning) { $smbSignPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' $smbSignProp = Get-ItemProperty -Path $smbSignPath -Name RequireSecuritySignature -ErrorAction SilentlyContinue $smbSignValue = $smbSignProp.RequireSecuritySignature if ($null -eq $smbSignValue) { $actualSign = 'Not Set' $status = 'DRIFT' } else { if ($smbSignValue -eq 1) { $actualSign = 'Enabled' $status = 'COMPLIANT' } else { $actualSign = 'Disabled' $status = 'DRIFT' } } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'SMB Signing Enforcement' Expected = 'Enabled' Actual = $actualSign Status = $status Severity = 'HIGH' ComputerName = $ComputerName } } # [CHECK 3] NTLMv2 Authentication (All profiles) $ntlmPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa' $ntlmProp = Get-ItemProperty -Path $ntlmPath -Name LmCompatibilityLevel -ErrorAction SilentlyContinue $ntlmLevel = $ntlmProp.LmCompatibilityLevel if ($null -ne $ntlmLevel) { if ($ntlmLevel -ge $expectedNTLMMin -and $ntlmLevel -le $expectedNTLMMax) { $status = 'COMPLIANT' } else { $status = 'DRIFT' } } else { $ntlmLevel = 'Not Set' $status = 'DRIFT' } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'NTLM Compatibility Level' Expected = "Level $expectedNTLMMin-$expectedNTLMMax (NTLMv2)" Actual = $ntlmLevel Status = $status Severity = 'HIGH' ComputerName = $ComputerName } # [CHECK 4] LDAP Signing (Recommended+) if ($checkLDAP) { $ldapPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\LDAP' $ldapProp = Get-ItemProperty -Path $ldapPath -Name LDAPClientIntegrity -ErrorAction SilentlyContinue $ldapValue = $ldapProp.LDAPClientIntegrity if ($null -eq $ldapValue) { $actualLDAP = 'Not Set' $status = 'DRIFT' } else { if ($ldapValue -eq 1) { $actualLDAP = 'Required' $status = 'COMPLIANT' } else { $actualLDAP = 'Not Required' $status = 'DRIFT' } } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'LDAP Signing' Expected = 'Required' Actual = $actualLDAP Status = $status Severity = 'MEDIUM' ComputerName = $ComputerName } } # [CHECK 5] LLMNR Disabled (Recommended+) if ($checkLLMNR) { $llmnrPath = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient' $llmnrProp = Get-ItemProperty -Path $llmnrPath -Name EnableMulticast -ErrorAction SilentlyContinue $llmnrValue = $llmnrProp.EnableMulticast if ($null -eq $llmnrValue) { $actualLLMNR = 'Not Configured (Default: Enabled)' $status = 'DRIFT' } else { if ($llmnrValue -eq 0) { $actualLLMNR = 'Disabled' $status = 'COMPLIANT' } else { $actualLLMNR = 'Enabled' $status = 'DRIFT' } } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'LLMNR (Link-Local Multicast Name Resolution)' Expected = 'Disabled' Actual = $actualLLMNR Status = $status Severity = 'MEDIUM' ComputerName = $ComputerName } } # [CHECK 6] SMB Encryption (Strict only) if ($checkSMBEncrypt) { $smbEncryptPath = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters' $smbEncryptProp = Get-ItemProperty -Path $smbEncryptPath -Name SMBEncryptionRequired -ErrorAction SilentlyContinue $smbEncryptValue = $smbEncryptProp.SMBEncryptionRequired if ($null -eq $smbEncryptValue) { $actualEncrypt = 'Not Set' $status = 'DRIFT' } else { if ($smbEncryptValue -eq 1) { $actualEncrypt = 'Required' $status = 'COMPLIANT' } else { $actualEncrypt = 'Not Required' $status = 'DRIFT' } } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'SMB Encryption' Expected = 'Required' Actual = $actualEncrypt Status = $status Severity = 'HIGH' ComputerName = $ComputerName } } # [CHECK 7] Kerberos Encryption (Strict only) if ($checkKerberos) { $kerberosPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters' $kerberosProp = Get-ItemProperty -Path $kerberosPath -Name SupportedEncryptionTypes -ErrorAction SilentlyContinue $kerberosValue = $kerberosProp.SupportedEncryptionTypes if ($null -eq $kerberosValue) { $actualKerberos = 'Not Set' $status = 'DRIFT' } else { $actualKerberos = "0x{0:X8}" -f $kerberosValue if ($kerberosValue -eq 0xFFFFFFFF -or $kerberosValue -ge 0xFFFF) { $status = 'COMPLIANT' } else { $status = 'DRIFT' } } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'Kerberos Encryption Types' Expected = '0xFFFFFFFF (All Modern Types)' Actual = $actualKerberos Status = $status Severity = 'MEDIUM' ComputerName = $ComputerName } } # [CHECK 8] TLS Protocol Hardening (Detailed mode in Recommended+, default in Strict) if ($checkTLS) { $tlsPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols' $sslDisabled = $true $tlsOldDisabled = $true foreach ($protocol in @('SSL 3.0', 'TLS 1.0', 'TLS 1.1')) { $protoPath = Join-Path -Path $tlsPath -ChildPath $protocol $serverProp = Get-ItemProperty -Path "$protoPath\Server" -Name Enabled -ErrorAction SilentlyContinue if ($null -ne $serverProp.Enabled -and $serverProp.Enabled -eq 1) { if ($protocol -eq 'SSL 3.0') { $sslDisabled = $false } else { $tlsOldDisabled = $false } } } if ($sslDisabled -and $tlsOldDisabled) { $tlsStatus = 'COMPLIANT' $tlsActual = 'All Disabled' } else { $tlsStatus = 'DRIFT' $tlsActual = 'Some Enabled' } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'Legacy TLS Protocols' Expected = 'SSL 3.0/TLS 1.0/1.1 Disabled' Actual = $tlsActual Status = $tlsStatus Severity = 'HIGH' ComputerName = $ComputerName } } # [CHECK 9] IPsec Status (Detailed mode in Strict only) if ($checkIPsec) { $ipsecStatus = (Get-NetFirewallProfile -Name Domain @remoteParams).PolicyStore -ne $null $ipsecCompliant = $ipsecStatus if ($ipsecCompliant) { $ipsecActual = 'Active' $ipsecStat = 'COMPLIANT' } else { $ipsecActual = 'Inactive' $ipsecStat = 'DRIFT' } $findings += [PSCustomObject]@{ Category = 'Network Security' Setting = 'IPsec Status' Expected = 'Active' Actual = $ipsecActual Status = $ipsecStat Severity = 'MEDIUM' ComputerName = $ComputerName } } # Filter results if requested if ($ReportDriftOnly) { $findings = $findings | Where-Object { $_.Status -eq 'DRIFT' } } # Log summary $driftCount = ($findings | Where-Object { $_.Status -eq 'DRIFT' }).Count if ($driftCount -gt 0) { Write-Log -Message "Network Security drift detected: $driftCount items on $ComputerName (Profile: $Profile)" ` -Level Warning -Caller $MyInvocation.MyCommand.Name } return $findings } catch { Write-Log -Message "Error checking network security on $ComputerName : $_" ` -Level Error -Caller $MyInvocation.MyCommand.Name throw } } |