Windows Security Worklet - NTLM, SMB, NetBIOS, CredSSP and Unquoted Search Path

One way to minimize the number of policies was to build a worklet that I’m using to apply security baseline changes to workstations. More importantly I’m using multiple PowerShell functions to control output to obtain a cleaner looking activity details when a host runs the worklet.

Each configuration change was a result of either a vulnerability scanner or baseline requirement. I’ve attempted to remember to put links in where applicable in the code.

Evaluation Code
# Security Team Workstation Configuration Baseline Evaluation Code

#region Rapid7 Vulnerability: Weak LAN Manager hashing permitted
Function Test-LMCompatibilityLevel {
    # Network security: LAN Manager authentication level
    # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/jj852207(v=ws.11)
    $path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
    $ntlm = Get-ItemProperty -Path $path -Name LmCompatibilityLevel -ErrorAction SilentlyContinue

    $compliant = switch ($ntlm.LmCompatibilityLevel)
    {
        0 {'No'} # Send LM & NTLM responses
        1 {'No'} # Send LM & NTLM – use NTLMv2 session security if negotiated
        2 {'No'} # Send NTLM response only
        3 {'No'} # Send NTLMv2 response only
        4 {'Yes'} # Send NTLMv2 response only. Refuse LM
        5 {'Yes'} # Send NTLMv2 response only. Refuse LM & NTLM
        Default {'No'}
    }

    IF($compliant -eq 'Yes'){
        return 0 # compliant
    }else{
        return 1 # non-compliant
    }
}
#endregion

#region Rapid7 Vulnerability: Microsoft CVE-2018-0886 CredSSP Remote Code Execution
function Test-CredSSP{
    # Following https://support.microsoft.com/en-us/help/4093492/credssp-updates-for-cve-2018-0886-march-13-2018
    $RegKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters'
    $Name = 'AllowEncryptionOracle'
    $CredSSP = (Get-ItemProperty $RegKey -Name $Name).$Name

    if (($CredSSP -ne 0)){
        return 1 # non-compliant
    }
    else{
        return 0 # compliant
    }
}
#endregion

#region Rapid7 Vulnerability: NetBIOS NBSTAT Traffic Amplification
function Test-NetBT{
    $regkey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces"
    $NICS = Get-ChildItem $regkey |foreach { Get-ItemProperty -Path "$regkey\$($_.pschildname)" -Name NetbiosOptions -ErrorAction SilentlyContinue}
    $i = 0
    foreach ($NIC in $NICS)
    {
        if ($NIC.NetbiosOptions -ne 2)
        {
            $i++
        }
    }

    IF($i -gt 0){
        return 1 # non-compliant
    }else{
        return 0 # compliant
    }
}
#endregion

#region Rapid7 Vulnerabilities & CIS Benchmark: Multiple SMB Findings
function Test-SMB{

    # Set counter to determine compliance across multiple configurations
    $i = 0

    # CIS Benchmark: 2.3.10.3. (L1) Ensure 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' is set to 'Enabled'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA'
    $Names = 'RestrictAnonymous','RestrictAnonymous'
    foreach ($Name in $Names){
        $value = (Get-ItemProperty $RegKey -Name $Name).$Name
        IF($value -ne 1){$i++}
    }

    # Rapid7: SMB: Service supports deprecated SMBv1 protocol
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $Name = 'SMB1'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 0){$i++}

    # Rapid7: SMB signing not required, SMB signing disabled
    $Name = 'RequireSecuritySignature'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){$i++}
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){$i++}

    if (($i -ne 0)){
        return 1 # non-compliant
    }else{
        return 0 # compliant
    }
}
#endregion

#region Rapid7 Vulnerability: Unquoted Search Path
function Test-UnquotedSearchPath{
    $i = 0
    $BaseKeys = "HKLM:\System\CurrentControlSet\Services",                                  #Services
                "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall",                #32bit Uninstalls
                "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"     #64bit Uninstalls
    #Blacklist for keys to ignore
    $BlackList = $Null
    #Create an ArrayList to store results in
    $Values = New-Object System.Collections.ArrayList
    #Discovers all registry keys under the base keys
    $DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty Name | %{($_.ToString().Split('\') | Select-Object -Skip 1) -join '\'}
    #Open the local registry
    $Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
    ForEach ($RegKey in $DiscKeys)
    {
        #Open each key with write permissions
        Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
        Catch { Write-Debug "Unable to open $RegKey" }
        #Test if registry key has values
        If ($ParentKey.ValueCount -gt 0)
        {
            $MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq "ImagePath" -or $_ -eq "UninstallString" }
            ForEach ($Match in $MatchedValues)
            {
                #RegEx that matches values containing .exe with a space in the exe path and no double quote encapsulation
                $ValueRegEx = '(^(?!\u0022).*\s.*\.[Ee][Xx][Ee](?<!\u0022))(.*$)'
                $Value = $ParentKey.GetValue($Match)
                #Test if value matches RegEx
                If ($Value -match $ValueRegEx)
                {
                    $RegType = $ParentKey.GetValueKind($Match)
                    #Uses the matches from the RegEx to build a new entry encapsulating the exe path with double quotes
                    $Correction = "$([char]34)$($Matches[1])$([char]34)$($Matches[2])"

                    #Attempt to correct the entry
                    $i++
                }
            }
        }
        $ParentKey.Close()
    }
    $Registry.Close()

    IF($i -eq 0){
        return 0 # compliant
    }else{
        return 1 # non-complinat
    }
}
#endregion


$i = 0
$i = $i + (Test-LMCompatibilityLevel) # Rapid7 Vulnerability: Weak LAN Manager hashing permitted
$i = $i + (Test-CredSSP) # Rapid7 Vulnerability: Microsoft CVE-2018-0886 CredSSP Remote Code Execution
$i = $i + (Test-NetBT) # Rapid7 Vulnerability: NetBIOS NBSTAT Traffic Amplification
$i = $i + (Test-UnquotedSearchPath) # Rapid7 Vulnerabilities & CIS Benchmark: Multiple SMB Findings
$i = $i + (Test-SMB) # Rapid7 Vulnerability: Unquoted Search Path

IF($i -gt 0){
    exit 1 # non-compliant
}else{
    exit 0 # compliant
}

Remediation Code
# Security Team Workstation Configuration Baseline Remediation Code

#region Rapid7 Vulnerability: Weak LAN Manager hashing permitted
Function Test-LMCompatibilityLevel {
    # Network security: LAN Manager authentication level
    # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/jj852207(v=ws.11)
    $path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
    $ntlm = Get-ItemProperty -Path $path -Name LmCompatibilityLevel -ErrorAction SilentlyContinue

    $compliant = switch ($ntlm.LmCompatibilityLevel)
    {
        0 {'No'} # Send LM & NTLM responses
        1 {'No'} # Send LM & NTLM – use NTLMv2 session security if negotiated
        2 {'No'} # Send NTLM response only
        3 {'No'} # Send NTLMv2 response only
        4 {'Yes'} # Send NTLMv2 response only. Refuse LM
        5 {'Yes'} # Send NTLMv2 response only. Refuse LM & NTLM
        Default {'No'}
    }

    IF($compliant -eq 'Yes'){
        return 0 # compliant
    }else{
        return 1 # non-compliant
    }
}
Function Set-LMCompatibilityLevel{
    # Network security: LAN Manager authentication level
    # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/jj852207(v=ws.11)
    # 2.3.11.7. (L1) Ensure 'Network security: LAN Manager authentication level' is set to 'Send NTLMv2 response only. Refuse LM&NTLM'
    $path = "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa"
    IF(!($path)){New-Item $path}

    $ntlm = Get-ItemProperty -Path $path -Name LmCompatibilityLevel -ErrorAction SilentlyContinue
    IF($ntlm){
        Set-ItemProperty -path $path -Name LmCompatibilityLevel -Value 5 | out-Null
    }else{ New-ItemProperty -path $path -Name LmCompatibilityLevel -Value 5 -PropertyType Dword | out-null }
}
IF((Test-LMCompatibilityLevel) -eq 1){Write-Output "NTLM Non-Compliant, Remediating. ";Set-LMCompatibilityLevel}
IF((Test-LMCompatibilityLevel) -eq 0){Write-Output "NTLM Configured... OK. "}else{Write-Output "NTLM Configuration Non-Compliant. Remediation FAILED. Giving Up."}
#endregion

#region Rapid7 Vulnerability: Microsoft CVE-2018-0886 CredSSP Remote Code Execution Vulnerability
function Test-CredSSP{
    # Following https://support.microsoft.com/en-us/help/4093492/credssp-updates-for-cve-2018-0886-march-13-2018
    $RegKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters'
    $Name = 'AllowEncryptionOracle'
    $CredSSP = (Get-ItemProperty $RegKey -Name $Name).$Name

    if (($CredSSP -ne 0)){
        return 1 # non-compliant
    }
    else{
        return 0 # compliant
    }
}
function Set-CredSSP{
    # Resolve Microsoft CVE-2018-0886: CredSSP Remote Code Execution Vulnerability
    $RegKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters'
    $Name = 'AllowEncryptionOracle'
    Set-ItemProperty $RegKey -Name $Name -Value 0 -Force
}
IF((Test-CredSSP) -eq 1){Write-Output "CredSSP Non-Compliant, Remediating. ";Set-CredSSP}
IF((Test-CredSSP) -eq 0){Write-Output "CredSSP Configured... OK. "}else{Write-Output "CredSSP Configuration Non-Compliant. Remediation FAILED. Giving Up."}
#endregion

#region Rapid7 Vulnerability: NetBIOS NBSTAT Traffic Amplification
function Test-NetBT{
    $regkey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces"
    $NICS = Get-ChildItem $regkey |foreach { Get-ItemProperty -Path "$regkey\$($_.pschildname)" -Name NetbiosOptions -ErrorAction SilentlyContinue}
    $i = 0
    foreach ($NIC in $NICS)
    {
        if ($NIC.NetbiosOptions -ne 2)
        {
            $i++
        }
    }

    IF($i -gt 0){
        return 1 # non-compliant
    }else{
        return 0 # compliant
    }
}
function Set-NetBT{
    $regkey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces"
    $NICS = Get-ChildItem $regkey |foreach { Get-ItemProperty -Path "$regkey\$($_.pschildname)" -Name NetbiosOptions -ErrorAction SilentlyContinue}
    $i = 0
    foreach ($NIC in $NICS)
    {
        if ($NIC.NetbiosOptions -ne 2)
        {
            Set-ItemProperty -Path "$regkey\$($NIC.PSChildName)" -Name NetbiosOptions -Value 2
        }
    }
}
IF((Test-NetBT) -eq 1){Write-Output "NetBIOS Non-Compliant, Remediating. ";Set-NetBT}
IF((Test-NetBT) -eq 0){Write-Output "NetBIOS Configured... OK. "}else{Write-Output "NetBIOS Configuration Non-Compliant. Remediation FAILED. Giving Up."}
#endregion

#region Rapid7 Vulnerabilities & CIS Benchmark: Multiple SMB Findings
function Test-SMB{

    # Set counter to determine compliance across multiple configurations
    $i = 0

    # CIS Benchmark: 2.3.10.3. (L1) Ensure 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' is set to 'Enabled'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA'
    $Names = 'RestrictAnonymous','RestrictAnonymous'
    foreach ($Name in $Names){
        $value = (Get-ItemProperty $RegKey -Name $Name).$Name
        IF($value -ne 1){$i++}
    }

    # Rapid7: SMB: Service supports deprecated SMBv1 protocol
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $Name = 'SMB1'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 0){$i++}

    # Rapid7: SMB signing not required, SMB signing disabled
    $Name = 'RequireSecuritySignature'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){$i++}
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){$i++}

    if (($i -ne 0)){
        return 1 # non-compliant
    }else{
        return 0 # compliant
    }
}
function Set-SMB{
    # CIS Benchmark: 2.3.10.3. (L1) Ensure 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' is set to 'Enabled'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA'
    $Names = 'RestrictAnonymous','RestrictAnonymous'
    foreach ($Name in $Names){
        $value = (Get-ItemProperty $RegKey -Name $Name).$Name
        IF($value -ne 1){
        Set-ItemProperty $RegKey -Name $Name -Value 1 -Force
        }
    }

    # Rapid7: SMB: Service supports deprecated SMBv1 protocol
    # CIS Benchmark: 18.3.3. (L1) Ensure 'Configure SMB v1 server' is set to 'Disabled'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $Name = 'SMB1'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 0){Set-ItemProperty $RegKey -Name $Name -Value 0}

    # Require SMB Signing both Client and Server
    $Name = 'RequireSecuritySignature'
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){Set-ItemProperty $RegKey -Name $Name -Value 1}
    $RegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters'
    $value = (Get-ItemProperty $RegKey -Name $Name).$Name
    IF($value -ne 1){Set-ItemProperty $RegKey -Name $Name -Value 1}
}
IF((Test-SMB) -eq 1){Write-Output "SMB Configured... Non-Compliant. Remediating. ";Set-SMB}
IF((Test-SMB) -eq 0){Write-Output "SMB Configured... OK"}else{Write-Output "SMB Configuration Non-Compliant. Remediation FAILED. Giving Up."}
#endregion

#region Rapid7 Vulnerability: Unquoted Search Path
function Test-UnquotedSearchPath{
    $i = 0
    $BaseKeys = "HKLM:\System\CurrentControlSet\Services",                                  #Services
                "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall",                #32bit Uninstalls
                "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"     #64bit Uninstalls
    #Blacklist for keys to ignore
    $BlackList = $Null
    #Create an ArrayList to store results in
    $Values = New-Object System.Collections.ArrayList
    #Discovers all registry keys under the base keys
    $DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty Name | %{($_.ToString().Split('\') | Select-Object -Skip 1) -join '\'}
    #Open the local registry
    $Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
    ForEach ($RegKey in $DiscKeys)
    {
        #Open each key with write permissions
        Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
        Catch { Write-Debug "Unable to open $RegKey" }
        #Test if registry key has values
        If ($ParentKey.ValueCount -gt 0)
        {
            $MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq "ImagePath" -or $_ -eq "UninstallString" }
            ForEach ($Match in $MatchedValues)
            {
                #RegEx that matches values containing .exe with a space in the exe path and no double quote encapsulation
                $ValueRegEx = '(^(?!\u0022).*\s.*\.[Ee][Xx][Ee](?<!\u0022))(.*$)'
                $Value = $ParentKey.GetValue($Match)
                #Test if value matches RegEx
                If ($Value -match $ValueRegEx)
                {
                    $RegType = $ParentKey.GetValueKind($Match)
                    #Uses the matches from the RegEx to build a new entry encapsulating the exe path with double quotes
                    $Correction = "$([char]34)$($Matches[1])$([char]34)$($Matches[2])"

                    #Attempt to correct the entry
                    $i++
                }
            }
        }
        $ParentKey.Close()
    }
    $Registry.Close()

    IF($i -eq 0){
        return 0 # compliant
    }else{
        return 1 # non-complinat
    }
}
function Set-UnquotedSearchPath{
    $BaseKeys = "HKLM:\System\CurrentControlSet\Services",                                  #Services
                "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall",                #32bit Uninstalls
                "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"     #64bit Uninstalls
    #Blacklist for keys to ignore
    $BlackList = $Null
    #Create an ArrayList to store results in
    $Values = New-Object System.Collections.ArrayList
    #Discovers all registry keys under the base keys
    $DiscKeys = Get-ChildItem -Recurse -Directory $BaseKeys -Exclude $BlackList -ErrorAction SilentlyContinue |
                Select-Object -ExpandProperty Name | %{($_.ToString().Split('\') | Select-Object -Skip 1) -join '\'}
    #Open the local registry
    $Registry = [Microsoft.Win32.RegistryKey]::OpenBaseKey('LocalMachine', 'Default')
    ForEach ($RegKey in $DiscKeys)
    {
        #Open each key with write permissions
        Try { $ParentKey = $Registry.OpenSubKey($RegKey, $True) }
        Catch { Write-Debug "Unable to open $RegKey" }
        #Test if registry key has values
        If ($ParentKey.ValueCount -gt 0)
        {
            $MatchedValues = $ParentKey.GetValueNames() | ?{ $_ -eq "ImagePath" -or $_ -eq "UninstallString" }
            ForEach ($Match in $MatchedValues)
            {
                #RegEx that matches values containing .exe with a space in the exe path and no double quote encapsulation
                $ValueRegEx = '(^(?!\u0022).*\s.*\.[Ee][Xx][Ee](?<!\u0022))(.*$)'
                $Value = $ParentKey.GetValue($Match)
                #Test if value matches RegEx
                If ($Value -match $ValueRegEx)
                {
                    $RegType = $ParentKey.GetValueKind($Match)
                    #Uses the matches from the RegEx to build a new entry encapsulating the exe path with double quotes
                    $Correction = "$([char]34)$($Matches[1])$([char]34)$($Matches[2])"
                    #Attempt to correct the entry
                    Try { $ParentKey.SetValue("$Match", "$Correction", [Microsoft.Win32.RegistryValueKind]::$RegType) }
                    Catch { Write-Debug "Unable to write to $ParentKey" }
                    #Add a hashtable containing details of corrected key to ArrayList
                    $Values.Add((New-Object PSObject -Property @{
                    "Name" = $Match
                    "Type" = $RegType
                    "Value" = $Value
                    "Correction" = $Correction
                    "ParentKey" = "HKEY_LOCAL_MACHINE\$RegKey"
                    })) | Out-Null
                }
            }
        }
        $ParentKey.Close()
    }
    $Registry.Close()
    $Values | Select-Object @{l='Timestamp';e={"$((Get-Date).ToShortDateString()) $((Get-Date).ToShortTimeString())"}},ParentKey,Value,Correction,Name,Type | export-csv C:\windows\temp\spreetail-it-unquoted.csv -NoTypeInformation -Append
}
IF((Test-UnquotedSearchPath) -eq 1){Write-Output "Unquoted Search Path Non-Compliant, Remediating. ";Set-UnquotedSearchPath}
IF((Test-UnquotedSearchPath) -eq 0){Write-Output "Unquoted Search Path Configured... OK."}else{Write-Output "Unquoted Search Path Configuration Non-Compliant. Remediation FAILED. Giving Up."}
#endregion
1 Like