Skip to content

Instantly share code, notes, and snippets.

@hpaul-osi
Last active July 2, 2021 02:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save hpaul-osi/011257c57a0fd9228bca9e0f1dde23f6 to your computer and use it in GitHub Desktop.
Save hpaul-osi/011257c57a0fd9228bca9e0f1dde23f6 to your computer and use it in GitHub Desktop.
Examples from Extreme PI System Hardening at PI World 2018
Extreme PI System Hardening
High value systems warrant hardcore hardening measures. The PI System resides at a critical junction, communicating across strict network boundaries. Under this paradigm, the PI System acts as a 'safe harbor' for data, defending critical systems by reducing the number of users inside the security perimeter while enabling growth in the number of users getting value from OT data. An application can only be as secure as its operating platform, so this session will start from the ground up. We will establish a solid foundation with advanced hardening measures for the Windows operating system that OSIsoft has collected over many years working with the platform, such as security baselines, PowerShell’s Desired State Configuration, and arcane corners of the Windows Advanced Firewall. With the platform locked down, we will explore application hardening measures built within and tailored to the PI System. Emphasis will be on using the latest technology and tools available to embrace agility and configuration as code. Examples from session demos will be available on GitHub for administrators who want try them at home.
https://piworld.osisoft.com/us2018/sessions
# We'll be using the core image
$TargetMachine = "pida.cyber.io"
# Establish a PSSession to the PI Data Archive
$pscore = New-PSSession -ComputerName $TargetMachine
# Go to demo working directory
sl ($home + "\Documents\demo")
# There are some modules we'll need for our baselines
$Modules = @(
'xPSDesiredStateConfiguration',
'AuditPolicyDSC',
'SecurityPolicyDSC',
'xStorage',
'xNetworking'
)
# We need to install a few prerequisites to run the DSC baselines
Invoke-Command -Session $pscore `
-FilePath .\Supporting1_InstallDscBaselinePrerequisites.ps1 `
-ArgumentList (,$Modules)
# Let's verify we installed the resources we think.
Invoke-Command -Session $pscore -ScriptBlock { Get-DscResource | select Module -Unique }
# Windows Security Baselines
# https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-security-baselines
# Security Compliance Toolkit
# https://www.microsoft.com/en-us/download/details.aspx?id=55319
# Blog Post for Windows Server 2016
# https://blogs.technet.microsoft.com/secguide/2016/10/17/security-baseline-for-windows-10-v1607-anniversary-edition-and-windows-server-2016/
# GPO {088E04EC-440C-48CB-A8D7-A89D0162FBFB} corresponds to Windows Server 2016 Member Server Baseline - Computer
$BaselineModule = "BaselineManagement"
Install-Module $BaselineModule -Verbose
Import-Module $BaselineModule -Verbose
# Let's set a few paths
$GPOPath = $home + "\Documents\Demo\MSBaseline\GPOs\{088E04EC-440C-48CB-A8D7-A89D0162FBFB}"
$ConfigName = 'DSCFromGPO'
$OutputPath = ('.\' + $ConfigName)
if(!(test-path $OutputPath)){ ni $OutputPath -it Directory }
# Create our DSC config from the baseline
ConvertFrom-GPO -OutputConfigurationScript -OutputPath $OutputPath -Path $GPOPath
# Look inside the created folder
ls $ConfigName
# Don't need the MOF file here; we'll compile on the target
ri ".\$ConfigName\localhost.mof"
# Let's look at the head of the file for anything interesting
$ConfigFile = ".\$ConfigName\DSCFromGPO.ps1"
gc $ConfigFile -TotalCount 20
gc $ConfigFile -Tail 20
# Now we can compile the config on the target server
Invoke-Command -Session $pscore -FilePath $ConfigFile
# Stepping in will give us a closer look.
Enter-PSSession -Session $pscore
# Let's get our current status before we go crazy throwing settings on the machine.
$before = Test-DscConfiguration .\DSCFromGPO -Verbose
# Let's look at how the result is structured
$before
$before | get-member | FT
# Let's take a look at what is already good
$before.ResourcesInDesiredState | select resourceid
# And what needs to be done
$before.ResourcesNotInDesiredState | select resourceid
# Now we can make it so
Start-DscConfiguration .\DSCFromGPO -Wait -Verbose
# Let's get our current status before we go crazy throwing settings on the machine.
$after = Test-DscConfiguration .\DSCFromGPO -Verbose
# Let's take a look at what is already good
$after.ResourcesInDesiredState | select resourceid
# And what needs to be done
$after.ResourcesNotInDesiredState | select resourceid
# We can explore the other cmdlets related to DSC
Get-Command -Module PSDesiredStateConfiguration
# I can also look at the LCM config
Get-DscLocalConfigurationManager
# We can see the status of our configurations
Get-DscConfigurationStatus
# I can pull all the configs pushed to the machine
$DSCConfigs = Get-DscConfiguration
# Let's dig into these records
$DSCConfigs | Get-Member
$DSCConfigs | select ConfigurationName -Unique
$DSCConfigs | select ResourceId | FT
# Just one to see all the detail
$DSCConfigs[0]
# I can run the whole set again, but I won't...
Start-DscConfiguration -UseExisting -Wait -Verbose
# Step back out
Exit-PSSession
# Clean up
Remove-PSSession -Session $pscore -Verbose
# Using the core image
$TargetMachine = "pida.cyber.io"
# Go to the working folder
sl ($home + "\Documents\demo")
# Location of PI Security DSC
$ModuleFolder = "$home\Documents\Utilities\PI-Security-Audit-Tools-v2.2.0.0-prerelease"
# Check the signatures so that we know we're using the right modules.
$signatures = gci $ModuleFolder -Recurse -File | ? { $_.Extension -in @('.ps1','.psm1','.psd1') } | Get-AuthenticodeSignature
$signatures | select Status, Path | FT -AutoSize
# Find the module
$ModuleFile = gci $ModuleFolder -File -Recurse -Filter 'PISYSAUDIT.psd1'
# Import the module
Import-Module $ModuleFile.FullName
# Let's see the full help
Get-Help PISysAudit -full
# Now look at the new report command
Get-Help New-PISysAuditReport -full
# Oh, let's check out conceptual help
Get-Help about_PISYSAUDIT
# Create a parameter and launcha an audit
$cpt = piauditparams $null $TargetMachine "pidataarchive"
piaudit -cpt $cpt
# Suppose we have another solution for monitoring, we can use SuppressCheckID
piaudit -cpt $cpt -SuppressCheckID @('AU10006')
# Using our core image
$TargetMachine = "pida.cyber.io"
# Establish a PSSession to the PI Data Archive
$pscore = new-pssession -ComputerName $TargetMachine
# Go to working folder
sl ("$home\Documents\demo")
# Downloaded DSC
# https://github.com/osisoft/PI-Security-Audit-Tools/releases
# Let's check if the file is blocked...
$Release = "PI-Security-DSC-v2.2.0.0-prerelease"
$DownloadZIP = "$home\Documents\Utilities\$Release.zip"
gi $DownloadZIP -Stream Zone.Identifier -ea SilentlyContinue
# Easier if we unblock it before unzipping
Unblock-File $DownloadZIP
# Extract
Expand-Archive $DownloadZIP -DestinationPath "$home\Documents\Utilities\" -Verbose -Force
# Set the module folder
$ModuleFolder = "$home\Documents\Utilities\$Release\"
# Check the signatures so that we know we're using the right modules.
$signatures = gci $ModuleFolder -Recurse -File | ? { $_.Extension -in @('.ps1','.psm1','.psd1') } | Get-AuthenticodeSignature
$signatures | select Status, Path | FT -AutoSize
# Copy PI Security DSC module over.
Copy-Item $ModuleFolder -ToSession $pscore -Destination C:\users\hpaul\documents\utilities -Verbose -Recurse -Force
# Enter PSSession
Enter-PSSession -Session $pscore
# Set ourselves in the utilities folder.
sl ("$home\Documents\utilities")
# Find the path to the PISecurityDSC module
$ModuleDirectory = gci '.\' -File -Recurse -Filter 'PISecurityDSC.psd1' | select DirectoryName
# Throw that in the PowerShell modules folder to install
Copy-Item $ModuleDirectory.DirectoryName `
-Destination ($env:ProgramFiles + "\WindowsPowerShell\Modules") `
-Recurse -Verbose -Force
# The computer should now be aware of the module resources
Get-DscResource -Module PISecurityDSC
# Switch to the folder with the sample configurations
sl ($ModuleDirectory.DirectoryName + "\..\..\Configuration")
# What's in there?
ls
# Look at the whole script.
Get-Content .\PIDataArchive_BasicWindowsImplementation.ps1 -TotalCount 100
# But if I dot source, then the session knows about my configuration function.
. .\PIDataArchive_BasicWindowsImplementation.ps1
# Then I can call the standard Get-Help on my configuration if the author did comment based help.
Get-Help PIDataArchive_BasicWindowsImplementation -Full
# Compile the config with the appropriate parameters based on the help.
PIDataArchive_BasicWindowsImplementation `
-PIAdministratorsADGroup "CS\PI Administrators" `
-PIUsersADGroup "CS\PI Readers" `
-PIBuffersADGroup "CS\PI Buffers" `
-PIInterfacesADGroup "CS\PI Interfaces" `
-PIPointsAnalysisCreatorADGroup "CS\PI Analytics" `
-PIWebAppsADGroup "CS\PI Web Apps"
Get-Content .\PIDataArchive_BasicWindowsImplementation\localhost.mof
# Test the configuration
$before = Test-DscConfiguration .\PIDataArchive_BasicWindowsImplementation -Verbose
$before.ResourcesInDesiredState | select ResourceId
$before.ResourcesNotInDesiredState | select ResourceId
# Make it so!
Start-DscConfiguration .\PIDataArchive_BasicWindowsImplementation -Wait -Verbose
# Audit the result
$after = Test-DscConfiguration .\PIDataArchive_BasicWindowsImplementation -Verbose
$after.ResourcesInDesiredState | select ResourceId
$after.ResourcesNotInDesiredState | select ResourceId
# Moving on to the next configuration
Get-Content .\PIDataArchive_AuditBaseline.ps1 -TotalCount 100
# Dot source
. .\PIDataArchive_AuditBaseline.ps1
# Then call the standard Get-Help on my configuration if the author did comment based help.
Get-Help PIDataArchive_AuditBaseline -Full
# Compile the config with the appropriate parameters based on the help.
PIDataArchive_AuditBaseline -NodeName localhost `
-OutputPath .\PIDataArchive_AuditBaseline `
-DaysToAllowEdit 90 `
-MaxQueryExecutionSeconds 60 `
-PIFirewallHostmasks @('10.0.0.*') `
-AuthenticationPolicy 3 `
-AutoTrustConfig 1
# Notice that an MOF file was generated.
get-content .\PIDataArchive_AuditBaseline\localhost.mof -Verbose
# Test and look at what is already in the desired state.
$before = Test-DscConfiguration .\PIDataArchive_AuditBaseline -Verbose
$before.ResourcesInDesiredState | select ResourceId
$before.ResourcesNotInDesiredState | select ResourceId
# Make it so!
Start-DscConfiguration .\PIDataArchive_AuditBaseline -Wait -Verbose
# Restart the PI Server so everything takes effect
& "$($env:PIServer)adm\pisrvstop.bat"
& "$($env:PIServer)adm\pisrvstart.bat"
# Audit the result
$after = Test-DscConfiguration .\PIDataArchive_AuditBaseline -Verbose
$after.ResourcesInDesiredState | select ResourceId
$after.ResourcesNotInDesiredState | select ResourceId
# When we're done, we leave.
Exit-PSSession
# You weren't raised in a barn. Close the door on your way out.
Get-PSSession -ComputerName $TargetMachine | Remove-PSSession
$NotepadExe = 'C:\Program Files\Notepad++\notepad++.exe'
# Go to demo folder.
sl "$home\Documents\demo"
# Use our core image
$TargetMachine = "pida.cyber.io"
# Establish PSSession
$pss = New-PSSession -ComputerName $TargetMachine
# Move the Attack Surface config over
Copy-Item .\Supporting4_AttackSurfaceReduction.ps1 `
-ToSession $pss `
-Destination c:\users\hpaul\documents
# The configuration contains several measures to reduce surface area
Start-Process $NotepadExe .\Supporting4_AttackSurfaceReduction.ps1
# Copy over the script
Copy-Item -Path .\Supporting4_AttackSurfaceReduction.ps1 `
-ToSession $pss `
-Destination C:\Users\hpaul\Documents\
# Copy the scraper over to the target node.
# Scraper from ESIC project at WSU
# https://github.com/ESIC-DA/AHA-Scraper
Copy-Item -Path ..\Utilities\AHA-Scraper `
-ToSession $pss `
-Destination C:\Users\hpaul\Documents\ `
-Recurse -Verbose -Force
# Hop into the PSSession
Enter-PSSession -Session $pss
# Compile the config
.\Supporting4_AttackSurfaceReduction.ps1
# Make it so
Start-DscConfiguration .\AttackSurfaceReduction -Wait -Verbose
# ByPass the ExecutionPolicy, storing the current one to reset afterward
$ExecPolicy = Get-ExecutionPolicy
Set-ExecutionPolicy bypass
# Navigate to the Scraper folder
Push-Location .\AHA-Scraper
# Call the Scraper
.\AHA-Scraper.ps1
# Rewind
Pop-Location;Set-ExecutionPolicy $ExecPolicy
# Hop out of the PS Session
Exit-PSSession
# Pull the analysis file from the session
Copy-Item -Path C:\Users\hpaul\Documents\AHA-Scraper\BinaryAnalysis.csv `
-FromSession $pss `
-Destination $home\Documents\demo\Supporting4_CoreInstallation.csv
# Now the PSSession can be closed
Remove-PSSession -Session $pss
# View results from Scraper with the GUI
# https://github.com/ESIC-DA/AHA-GUI
java -jar .\AHA\AHA-GUI.jar inputfile=Supporting4_CoreInstallation.csv
# View another example with Desktop environment
java -jar .\AHA\AHA-GUI.jar inputfile=Supporting4_DEInstallation.csv
# Go to the demo folder
sl "$home\documents\demo"
# Target the core machine
$TargetMachine = "pida.cyber.io"
# Enter a PSSession
$pss = New-PSSession -ComputerName $TargetMachine
# Copy over the VBS Example
Copy-Item -Path .\Supporting5_PINetMgrWSH.vbs -ToSession $pss -Destination C:\Users\hpaul\Documents -Verbose
$NotepadExe = 'C:\Program Files\Notepad++\notepad++.exe'
# Look at the VBS example
Start-Process $NotepadExe .\Supporting5_PINetMgrWSH.vbs
<#
Switch to the core machine to run it!
#>
# Look at the native PS example
Start-Process $NotepadExe .\Supporting5_PINetMgrWSH.ps1
# Now run it and see what the rules look like it creates
Invoke-Command -Session $pss -FilePath .\Supporting5_PINetMgrWSH.ps1
<#
Switch to the core machine to look at the result!
#>
# Look at the DSC Example
Start-Process $NotepadExe .\Supporting5_PINetMgrWSHDSC.ps1
# Invoke DSC example to compile it
Invoke-Command -Session $pss -FilePath .\Supporting5_PINetMgrWSHDSC.ps1
# Run the test
Invoke-Command -Session $pss {Test-DscConfiguration .\PINetMgrWSH -Verbose}
# Verify the PI Data Archive is still remotely accessible
Connect-PIDataArchive -PIDataArchiveMachineName $TargetMachine
$TargetMachine = "pida.cyber.io"
$NotepadExe = 'C:\Program Files\Notepad++\notepad++.exe'
# Get our new PSSession
$pss = New-PSSession -ComputerName $TargetMachine
# Per KB01062 there are recommended exclusions for PI Data Archive.
# Look at the AV exclusion script
Start-Process $NotepadExe .\Supporting6_AntivirusExclusions.ps1
# Run the script to add exclusions
Invoke-Command -Session $pss -FilePath .\Supporting6_AntivirusExclusions.ps1
$exclusions = Invoke-Command -Session $pss {Get-MpPreference | select ExclusionPath}
$exclusions.ExclusionPath
# Look at the Code Integrity Policy Script
Start-Process $NotepadExe .\Supporting6_NewCodeIntegrityPolicy.ps1
Start-Process $NotepadExe .\Supporting6_MSBlockSiPolicy.xml
Start-Process $NotepadExe .\Supporting6_PublisherSiPolicy.xml
# Extract signers from CIPolicy XML
$policyConfig = ".\Supporting6_PublisherSiPolicy.xml"
[xml]$PolicyContents = Get-Content $policyConfig
$PolicyContents.SiPolicy.Signers.Signer `
| Select Name, @{
Name="Publisher";
Expression={$_.CertPublisher.Value}
},
@{
Name="Root";
Expression={$_.CertRoot.Value}
} -Unique `
| FT -AutoSize | out-string -Width 4096 `
| out-file Supporting6_PublisherList.txt
# View List of Publishers
Start-Process $NotepadExe .\Supporting6_PublisherList.txt
# Move the policy XML files over.
Copy-Item -Path .\Supporting6_MSBlockSiPolicy.xml `
-ToSession $pss `
-Destination C:\Users\hpaul\Documents\MSBlockSiPolicy.xml `
-Force -Verbose
Copy-Item -Path .\Supporting6_PublisherSiPolicy.xml `
-ToSession $pss `
-Destination C:\Users\hpaul\Documents\PublisherSiPolicy.xml `
-Force -Verbose
# Run the script to merge and deploy the policies
Invoke-Command -Session $pss -FilePath .\Supporting6_NewCodeIntegrityPolicy.ps1
# !Place our malicious program after generating the policy!
Copy-Item -Path $home\Documents\Demo\Supporting6_test.nosig.exe `
-ToSession $pss `
-Destination C:\Users\hpaul\Documents\test.nosig.exe
# Look at script to enabling VBS
Start-Process $NotepadExe .\Supporting6_EnableVBS.ps1
# Enable and restart to take effect
Invoke-Command -Session $pss .\Supporting6_EnableVBS.ps1
Invoke-Command -Session $pss { Start-DscConfiguration .\EnableVBS -Wait -Verbose }
Invoke-Command -Session $pss { Restart-Computer }
Remove-PSSession -Session $pss
# Check if it has bounced back...
Test-NetConnection $TargetMachine -port 5985
<#
Jump over to the PI Data Archive to test
#>
# Establish a new session
$pss = New-PSSession -ComputerName $TargetMachine
# Get Status with msinfo32 or Get-CimInstance below
Invoke-Command -Session $pss {Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard}
# Get audit events
Invoke-Command -Session $pss {
Get-WinEvent -ProviderName 'Microsoft-Windows-CodeIntegrity' `
| Where-Object { $_.Id -eq 3077 } `
| Format-List
}
# Note the Language Mode has changed
Invoke-Command -Session $pss { $ExecutionContext.SessionState.LanguageMode }
# Grab the binary analysis file.
Invoke-Command -Session $pss `
{gc C:\Users\hpaul\Documents\AHA-Scraper\BinaryAnalysis.csv} | `
Out-File $home\Documents\demo\Supporting4_CoreHardened.csv -Encoding utf8
# Look at the visualization.
java -jar .\AHA\AHA-GUI.jar inputfile=Supporting4_CoreHardened.csv
Remove-PSSession -Session $pss
<#
.SYNOPSIS
Installs specified modules from PSGallery.
.DESCRIPTION
This function is intented to install resource modules required by DSC baselines. It will also add NuGet and PSGallery if not already added.
.PARAMETER RequiredModules
String array of module names to pull from PSGallery
.EXAMPLE
.\Install-DscBaselinePrerequisites.ps1
#>
param(
[string[]]$RequiredModules = @(
'xPSDesiredStateConfiguration'
)
)
# NuGet required to retrieve resources
if(Get-PackageProvider -ListAvailable -Name NuGet -ErrorAction SilentlyContinue)
{
Write-Output "NuGet Package located"
}
else
{
Write-Output "Attempting to install the prerequisite NuGet Package"
Install-PackageProvider -Name NuGet -Confirm -Verbose
}
if($(Get-PSRepository -Name PSGallery).InstallationPolicy -eq 'Trusted')
{
Write-Output "PSGallery InstallationPolicy is already Trusted"
}
else
{
Write-Output "Setting PSGallery InstallationPolicy to Trusted"
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -Verbose
}
# Pull in required modules
Find-Module $RequiredModules | Install-Module -Verbose
Configuration AttackSurfaceReduction
{
param(
[string]$NodeName="localhost"
)
Import-DscResource -ModuleName xNetworking, PSDesiredStateConfiguration
Node $NodeName
{
#region Networking
$InterfaceAlias = "Microsoft Hyper-V Network Adapter #2"
xNetBIOS DisableNetBIOS
{
InterfaceAlias = $InterfaceAlias
Setting = 'Disable'
}
$FirewallProfiles = @(
'Private',
'Public',
'Domain'
)
Foreach($Profile in $FirewallProfiles)
{
xFirewallProfile ("FirewallProfile_" + $Profile)
{
Name = $Profile
Enabled = 'True'
DefaultInboundAction = 'Block'
DefaultOutboundAction = 'Block'
AllowInboundRules = 'True'
NotifyOnListen = 'True'
LogFileName = '%systemroot%\system32\LogFiles\Firewall\pfirewall.log'
LogMaxSizeKilobytes = 20480
LogAllowed = 'False'
LogBlocked = 'True'
}
}
# Firewall - custom rules to enable
$PINetMgrProgram = ($env:piserver + "bin\pinetmgr.exe")
xFirewall PIDataArchive_ClientConnections_In
{
Direction = 'Inbound'
Name = 'PI-Data-Archive-PINET-TCP-In'
DisplayName = 'PI Data Archive PINET (TCP-In)'
Description = 'Inbound rule for PI Data Archive to allow PINET traffic.'
Group = 'PI System'
Enabled = 'True'
Action = 'Allow'
Protocol = 'TCP'
Service = "PINetMgr"
Program = $PINetMgrProgram
LocalPort = '5450'
Ensure = 'Present'
}
xFirewall PIDataArchive_ClientConnections_Out
{
Direction = 'Outbound'
Name = 'PI-Data-Archive-PINET-TCP-Out'
DisplayName = 'PI Data Archive PINET (TCP-Out)'
Description = 'Outbound rule for PI Data Archive to allow PINET traffic.'
Group = 'PI System'
Enabled = 'True'
Action = 'Allow'
Protocol = 'TCP'
Service = "PINetMgr"
Program = $PINetMgrProgram
RemotePort = '49152-65535'
Ensure = 'Present'
}
[string[]]$FirewallRulesEnabledByDefault = @(
"WINRM-HTTP-In-TCP",
"CoreNet-ICMP4-DUFRAG-In",
"CoreNet-IGMP-In",
"CoreNet-IGMP-Out",
"CoreNet-Teredo-In",
"CoreNet-Teredo-Out",
"CoreNet-IPHTTPS-In",
"CoreNet-IPHTTPS-Out",
"CoreNet-GP-NP-Out-TCP",
"CoreNet-GP-Out-TCP",
"CoreNet-DNS-Out-UDP",
"CoreNet-GP-LSASS-Out-TCP"
)
# Firewall - infrastructure rules to enable
ForEach($rule in $FirewallRulesEnabledByDefault)
{
xFirewall $rule
{
Name = $rule
Enabled = 'True'
Ensure = 'Present'
}
}
[string[]]$FirewallRulesDisabledByDefault = @(
"vm-monitoring-dcom",
"vm-monitoring-icmpv4",
"vm-monitoring-icmpv6",
"vm-monitoring-nb-session",
"vm-monitoring-rpc",
"SNMPTRAP-In-UDP",
"SNMPTRAP-In-UDP-NoScope",
"Wininit-Shutdown-In-Rule-TCP-RPC",
"Wininit-Shutdown-In-Rule-TCP-RPC-EPMapper",
"EventForwarder-In-TCP",
"EventForwarder-RPCSS-In-TCP",
"NETDIS-UPnPHost-In-TCP",
"NETDIS-UPnPHost-Out-TCP",
"NETDIS-NB_Name-In-UDP",
"NETDIS-NB_Name-Out-UDP",
"NETDIS-NB_Datagram-In-UDP",
"NETDIS-NB_Datagram-Out-UDP",
"NETDIS-WSDEVNTS-In-TCP",
"NETDIS-WSDEVNTS-Out-TCP",
"NETDIS-WSDEVNT-In-TCP",
"NETDIS-WSDEVNT-Out-TCP",
"NETDIS-SSDPSrv-In-UDP",
"NETDIS-SSDPSrv-Out-UDP",
"NETDIS-UPnP-Out-TCP",
"NETDIS-FDPHOST-In-UDP",
"NETDIS-FDPHOST-Out-UDP",
"NETDIS-LLMNR-In-UDP",
"NETDIS-LLMNR-Out-UDP",
"NETDIS-FDRESPUB-WSD-In-UDP",
"NETDIS-FDRESPUB-WSD-Out-UDP",
"Netlogon-NamedPipe-In",
"Netlogon-TCP-RPC-In",
"FPSSMBD-iWARP-In-TCP",
"RemoteTask-In-TCP",
"RemoteTask-RPCSS-In-TCP",
"WINRM-HTTP-Compat-In-TCP",
"Microsoft-Windows-PeerDist-HttpTrans-In",
"Microsoft-Windows-PeerDist-HttpTrans-Out",
"Microsoft-Windows-PeerDist-WSD-In",
"Microsoft-Windows-PeerDist-WSD-Out",
"Microsoft-Windows-PeerDist-HostedServer-In",
"Microsoft-Windows-PeerDist-HostedServer-Out",
"Microsoft-Windows-PeerDist-HostedClient-Out",
"RemoteDesktop-UserMode-In-TCP",
"RemoteDesktop-UserMode-In-UDP",
"RemoteDesktop-Shadow-In-TCP",
"RRAS-GRE-In",
"RRAS-GRE-Out",
"RRAS-L2TP-In-UDP",
"RRAS-L2TP-Out-UDP",
"RRAS-PPTP-In-TCP",
"RRAS-PPTP-Out-TCP",
"RVM-VDS-In-TCP",
"RVM-VDSLDR-In-TCP",
"RVM-RPCSS-In-TCP",
"MsiScsi-In-TCP",
"MsiScsi-Out-TCP",
"FPS-NB_Session-In-TCP",
"FPS-NB_Session-Out-TCP",
"FPS-SMB-In-TCP",
"FPS-SMB-Out-TCP",
"FPS-NB_Name-In-UDP",
"FPS-NB_Name-Out-UDP",
"FPS-NB_Datagram-In-UDP",
"FPS-NB_Datagram-Out-UDP",
"FPS-SpoolSvc-In-TCP",
"FPS-RPCSS-In-TCP",
"FPS-ICMP4-ERQ-In",
"FPS-ICMP4-ERQ-Out",
"FPS-ICMP6-ERQ-In",
"FPS-ICMP6-ERQ-Out",
"FPS-LLMNR-In-UDP",
"FPS-LLMNR-Out-UDP",
"RemoteEventLogSvc-In-TCP",
"RemoteEventLogSvc-NP-In-TCP",
"RemoteEventLogSvc-RPCSS-In-TCP",
"SPPSVC-In-TCP",
"PerfLogsAlerts-PLASrv-In-TCP",
"PerfLogsAlerts-DCOM-In-TCP",
"PerfLogsAlerts-PLASrv-In-TCP-NoScope",
"PerfLogsAlerts-DCOM-In-TCP-NoScope",
"SLBM-MUX-IN-TCP",
"RemoteSvcAdmin-In-TCP",
"RemoteSvcAdmin-NP-In-TCP",
"RemoteSvcAdmin-RPCSS-In-TCP",
"TPMVSCMGR-RPCSS-In-TCP-NoScope",
"TPMVSCMGR-Server-In-TCP-NoScope",
"TPMVSCMGR-Server-Out-TCP-NoScope",
"TPMVSCMGR-RPCSS-In-TCP",
"TPMVSCMGR-Server-In-TCP",
"TPMVSCMGR-Server-Out-TCP",
"MSDTC-In-TCP",
"MSDTC-Out-TCP",
"MSDTC-KTMRM-In-TCP",
"MSDTC-RPCSS-In-TCP",
"RemoteFwAdmin-In-TCP",
"RemoteFwAdmin-RPCSS-In-TCP",
"WMI-RPCSS-In-TCP",
"WMI-WINMGMT-In-TCP",
"WMI-WINMGMT-Out-TCP",
"WMI-ASYNC-In-TCP"
)
$FirewallRulesDisabledBySelection = @(
"AllJoyn-Router-In-TCP",
"AllJoyn-Router-Out-TCP",
"AllJoyn-Router-In-UDP",
"AllJoyn-Router-Out-UDP",
"WINRM-HTTP-In-TCP-PUBLIC",
"Microsoft-Windows-Unified-Telemetry-Client",
"CoreNet-ICMP6-DU-In",
"CoreNet-ICMP6-PTB-In",
"CoreNet-ICMP6-PTB-Out",
"CoreNet-ICMP6-TE-In",
"CoreNet-ICMP6-TE-Out",
"CoreNet-ICMP6-PP-In",
"CoreNet-ICMP6-PP-Out",
"CoreNet-ICMP6-NDS-In",
"CoreNet-ICMP6-NDS-Out",
"CoreNet-ICMP6-NDA-In",
"CoreNet-ICMP6-NDA-Out",
"CoreNet-ICMP6-RA-In",
"CoreNet-ICMP6-RA-Out",
"CoreNet-ICMP6-RS-In",
"CoreNet-ICMP6-RS-Out",
"CoreNet-ICMP6-LQ-In",
"CoreNet-ICMP6-LQ-Out",
"CoreNet-ICMP6-LR-In",
"CoreNet-ICMP6-LR-Out",
"CoreNet-ICMP6-LR2-In",
"CoreNet-ICMP6-LR2-Out",
"CoreNet-ICMP6-LD-In",
"CoreNet-ICMP6-LD-Out",
"CoreNet-IPv6-In",
"CoreNet-IPv6-Out",
"CoreNet-DHCPV6-In",
"CoreNet-DHCPV6-Out",
"CoreNet-DHCP-In",
"CoreNet-DHCP-Out",
"MDNS-In-UDP",
"MDNS-Out-UDP"
)
# Firewall - infrastructurs rules to disable
$FirewallRulesToDisable = $FirewallRulesDisabledByDefault + $FirewallRulesDisabledBySelection
ForEach($rule in $FirewallRulesToDisable)
{
xFirewall $rule
{
Name = $rule
Enabled = 'False'
Ensure = 'Present'
}
}
#endregion
#region Registry
$RegistryKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters\'
Registry "$RegistryKey\DisabledComponents"
{
Ensure = 'Present'
Key = $RegistryKey
ValueName = 'DisabledComponents'
ValueData = 255
ValueType = 'DWORD'
}
$RegistryKey = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\'
Registry "$RegistryKey\EnableMulticast"
{
Ensure = 'Present'
Key = $RegistryKey
ValueName = 'EnableMulticast'
ValueData = 0
ValueType = 'DWORD'
}
#endregion
#region Windows Features
[string[]]$ApprovedFeatures = @(
'FileAndStorage-Services',
'Storage-Services',
'NET-Framework-45-Features',
'NET-Framework-45-Core',
'NET-WCF-Services45',
'NET-WCF-TCP-PortSharing45',
'EnhancedStorage',
'Windows-Defender-Features',
'Windows-Defender',
'PowerShellRoot',
'PowerShell',
'WoW64-Support'
)
$AllFeatures = Get-WindowsFeature | Select-Object -ExpandProperty Name
Foreach($Feature in $AllFeatures)
{
if(!($Feature -in $ApprovedFeatures))
{
WindowsFeatureSet $( $Feature + '_Disable' )
{
Name = $Feature
Ensure = 'Absent'
}
}
}
#endregion
#region Windows Services
[String[]]$ServicesToDisable = @()
# Optional PI services to disable
$ServicesToDisable += @(
"PIAFLink",
"PIBaGen",
"pibatch",
"pibufss",
"PIDirectoryPublisher",
"pilogsrv",
"pilogsrvX64",
"pirecalc"
)
# Remaining OS Services not needed for PI Data Archive.
$ServicesToDisable += @(
"AppMgmt",
"DiagTrack",
"sacsvr",
"SNMPTRAP",
"seclogon",
"WinHttpAutoProxySvc"
)
# Services disabled by default
$ServicesToDisable += @(
"tzautoupdate",
"Browser",
"AppVClient",
"NetTcpPortSharing",
"CscService",
"RemoteAccess",
"SCardSvr",
"UevAgentService"
"WSearch"
)
# Services MS recommends disabling
$ServicesToDisable += @(
"XblAuthManager",
"XblGameSave"
)
# Services MS indicates as "OK to disable"
$ServicesToDisable += @(
"AxInstSV",
"bthserv",
"CDPUserSvc",
"PimIndexMaintenanceSvc"
"dmwappushservice",
"MapsBroker",
"lfsvc",
"SharedAccess",
"lltdsvc",
"wlidsvc",
"NgcSvc",
"NgcCtnrSvc",
"NcbService",
"PhoneSvc",
"PcaSvc",
"QWAVE",
"RmSvc",
"SensorDataService",
"SensrSvc",
"SensorService",
"ShellHWDetection",
"ScDeviceEnum",
"SSDPSRV",
"WiaRpc",
"OneSyncSvc",
"TabletInputService",
"upnphost",
"UserDataSvc",
"UnistoreSvc",
"WalletService",
"Audiosrv",
"AudioEndpointBuilder",
"FrameServer",
"stisvc",
"wisvc",
"icssvc",
"WpnService",
"WpnUserService"
)
# Services OK to disable if not a DC or print server
$ServicesToDisable += @('Spooler')
# Services OK to disable if not a print server
$ServicesToDisable += @('PrintNotify')
$InstalledServices = Get-Service
foreach($Service in $ServicesToDisable)
{
if($InstalledServices.Name -contains $Service)
{
Service $( 'DisabledService_' + $Service )
{
Name = $Service
StartupType = "Disabled"
State = "Stopped"
}
}
}
#endregion
}
}
AttackSurfaceReduction
# PS script for WSH
$Program = "%piserver%bin\pinetmgr.exe"
$Service = "PINetMgr"
$LocalPort = "5450"
$RemotePort = "49152-65535"
$Protocol = "TCP"
$WSHRules = @(
@{
Name = "Inbound service restriction rule for $Service"
Action = "Block"
Direction = "Inbound"
},
@{
Name = "Outbound service restriction rule for $Service"
Action = "Block"
Direction = "Outbound"
},
@{
Name = "Allow only TCP $LocalPort inbound to $Service"
Action = "Allow"
Direction = "Inbound"
Protocol = $Protocol
LocalPort = $LocalPort
},
@{
Name = "Allow only TCP $RemotePort outbound from $Service"
Action = "Allow"
Direction = "Outbound"
Protocol = $Protocol
RemotePort = $RemotePort
}
)
# Loop through the rules and apply.
foreach($Rule in $WSHRules)
{
# Include the proper scope on each rule
$Rule += @{
DisplayName = $Rule.Name
Program = $Program
Service = $Service
Enabled = "TRUE"
PolicyStore = "ConfigurableServiceStore"
}
New-NetFirewallRule @Rule
}
' VB script for WSH for PINetMgr
' MSDN has documented examples
' Ref: Restricting Service, WFAS on MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366327(v=vs.85).aspx)
option explicit
' IP protocol
const NET_FW_IP_PROTOCOL_TCP = 6
' Action
const NET_FW_ACTION_ALLOW = 1
' Direction
const NET_FW_RULE_DIR_IN = 1
const NET_FW_RULE_DIR_OUT = 2
' Create the FwPolicy2 object.
Dim fwPolicy2
Set fwPolicy2 = CreateObject("HNetCfg.FwPolicy2")
' Get the Service Restriction object for the local firewall policy.
Dim ServiceRestriction
Set ServiceRestriction = fwPolicy2.ServiceRestriction
' Put in block-all inbound and block-all outbound Windows Service Hardening (WSH) networking rules for the service
ServiceRestriction.RestrictService "PINetMgr", "%piserver%\bin\pinetmgr.exe", TRUE, FALSE
' Get the collection of Windows Service Hardening networking rules
Dim wshRules
Set wshRules = ServiceRestriction.Rules
' Add inbound WSH allow rule for service PINetMgr
Dim NewInboundRule
Set NewInboundRule = CreateObject("HNetCfg.FWRule")
NewInboundRule.Name = "Allow only TCP 5450 inbound to service"
NewInboundRule.ApplicationName = "%piserver%bin\pinetmgr.exe"
NewInboundRule.ServiceName = "PINetMgr"
NewInboundRule.Protocol = NET_FW_IP_PROTOCOL_TCP
NewInboundRule.LocalPorts = 5450
NewInboundRule.Action = NET_FW_ACTION_ALLOW
NewInboundRule.Direction = NET_FW_RULE_DIR_IN
NewInboundRule.Enabled = true
' Add the inbound allow rule
wshRules.Add NewInboundRule
' Add outbound WSH allow rules for PINetMgr
Dim NewOutboundRule
Set NewOutboundRule = CreateObject("HNetCfg.FWRule")
NewOutboundRule.Name = "Allow outbound traffic from service"
NewOutboundRule.ApplicationName = "%piserver%bin\pinetmgr.exe"
NewOutboundRule.ServiceName = "PINetMgr"
NewOutboundRule.Protocol = NET_FW_IP_PROTOCOL_TCP
NewOutboundRule.RemotePorts = "49152-65535"
NewOutboundRule.Action = NET_FW_ACTION_ALLOW
NewOutboundRule.Direction = NET_FW_RULE_DIR_OUT
NewOutboundRule.Enabled = true
' Add the outbound allow rule
wshRules.Add NewOutboundRule
Configuration PINetMgrWSH
{
param(
$ComputerName="localhost",
$Service="PINetMgr",
$Program="%piserver%bin\pinetmgr.exe",
$LocalPort = "5450",
$RemotePort = "49152-65535",
[ValidateSet("TCP","UDP")]
$Protocol = "TCP"
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node $ComputerName
{
switch($Protocol)
{
"TCP" {$ProtocolID = "6"}
"UDP" {$ProtocolID = "17"}
}
$ConfigurableServiceStore = "HKLM:\SYSTEM\CurrentControlSet\services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Configurable\System"
$Version = "v2.26"
$AllowInRuleName = "Allow only $Protocol $LocalPort inbound to $Service"
Registry $AllowInRuleName
{
Ensure = 'Present'
Key = $ConfigurableServiceStore
ValueData = "$Version|Action=Allow|Active=TRUE|Dir=In|" + `
"Protocol=$ProtocolID|" + `
"LPort=$LocalPort|" + `
"App=$Program|" + `
"Svc=$Service|" + `
"Name=$AllowInRuleName|"
ValueName = $AllowInRuleName
ValueType = 'String'
}
$AllowOutRuleName = "Allow only $Protocol $RemotePort outbound from $Service"
Registry $AllowOutRuleName
{
Ensure = 'Present'
Key = $ConfigurableServiceStore
ValueData = "$Version|Action=Allow|Active=TRUE|Dir=Out|" + `
"Protocol=$ProtocolID|" + `
"RPort2_10=$RemotePort|" + `
"App=$Program|" + `
"Svc=$Service|" + `
"Name=$AllowOutRuleName|"
ValueName = $AllowOutRuleName
ValueType = 'String'
}
$RestrictInRuleName = "Inbound service restriction rule for $Service"
Registry $RestrictInRuleName
{
Ensure = 'Present'
Key = $ConfigurableServiceStore
ValueData = "$Version|Action=Block|Active=TRUE|Dir=In|" + `
"App=$Program|" + `
"Svc=$Service|" + `
"Name=$RestrictInRuleName|"
ValueName = $RestrictInRuleName
ValueType = 'String'
}
$RestrictOutRuleName = "Outbound service restriction rule for $Service"
Registry $RestrictOutRuleName
{
Ensure = 'Present'
Key = $ConfigurableServiceStore
ValueData = "$Version|Action=Block|Active=TRUE|Dir=Out|" + `
"App=$Program|" + `
"Svc=$Service|" + `
"Name=$RestrictOutRuleName|"
ValueName = $RestrictOutRuleName
ValueType = 'String'
}
}
}
PINetMgrWSH
# Tested with Server 2016 and Server 2012 R2, PS 4 and PS 5
Configuration StrongCipherSettings
{
param
(
$ComputerName = "localhost",
# TLS/SSL Security Considerations
# https://technet.microsoft.com/en-us/library/dn786446(v=ws.11).aspx
$schannelProtocols = @{
"PCT 1.0"=$false;
"SSL 2.0"=$false;
"SSL 3.0"=$false;
"TLS 1.0"=$false;
"TLS 1.1"=$true;
"TLS 1.2"=$true
},
$schannelCiphers = @{
"NULL"=$false;
"DES 56/56"=$false;
"RC2 40/128"=$false;
"RC2 56/128"=$false;
"RC2 128/128"=$false;
"RC4 40/128"=$false;
"RC4 56/128"=$false;
"RC4 64/128"=$false;
"RC4 128/128"=$false;
"Triple DES 168"=$true;
"AES 128/128"=$true;
"AES 256/256"=$true
},
[string[]]$cipherSuites = @(
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384_P384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256_P256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA_P256",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA"
)
)
Import-DSCResource -ModuleName 'xPSDesiredStateConfiguration'
Node $ComputerName
{
# Value of 0 disables, 1 enables protocol or cipher
# https://technet.microsoft.com/en-us/library/dn786418(v=ws.11).aspx#BKMK_SchannelTR_Ciphers
$EnabledValue = "1"
$DisabledValue = "0"
$cryptographyKeyPath = 'HKLM:\System\CurrentControlSet\Control\Cryptography\Configuration\Local\SSL\00010002\'
xRegistry $($cryptographyKeyPath + 'Functions')
{
ValueName = 'Functions'
ValueType = 'MultiString'
Key = $cryptographyKeyPath
ValueData = $cipherSuites
Force = $true
}
$schannelKeyPath = "HKLM:\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\"
foreach ($cipher in $schannelCiphers.GetEnumerator())
{
if($cipher.Value) { $ValueData = $EnabledValue }
else { $ValueData = $DisabledValue }
$TargetPath = $($schannelKeyPath + 'Ciphers\' + $cipher.Name)
xRegistry $($TargetPath + '\Enabled')
{
ValueName = 'Enabled'
ValueType = 'DWORD'
Key = $TargetPath
ValueData = $ValueData
Force = $true
}
}
foreach ($protocol in $schannelProtocols.GetEnumerator())
{
if($protocol.Value) { $ValueData = $EnabledValue }
else { $ValueData = $DisabledValue }
foreach($Role in @('Server','Client'))
{
$TargetPath = $($schannelKeyPath + 'Protocols\' + $protocol.Name + '\' + $Role)
xRegistry $($TargetPath + '\Enabled')
{
ValueName = 'Enabled'
ValueType = 'DWORD'
Key = $TargetPath
ValueData = $ValueData
Force = $true
}
}
}
}
}
StrongCipherSettings
# Add PI Data Archive antivirus exceptions per KB01062 to Windows Defender in Windows 10 and Server 2016
param(
[string]$ArchiveFolder = "C:\Program Files\PI\arc\",
[string]$ArchiveFileExtenstion = ".arc",
[string]$QueueFolder = "C:\Program Files\PI\queue\"
)
# PI Message logs
$Logs = ($env:PISERVER + "log\*.dat")
# Archive files
$Archives = ($ArchiveFolder + "*" + $ArchiveFileExtenstion)
# Annotation files associated with archives
$ArchiveAnnotations = ($Archives + ".ann")
# Archives for future data
$FutureArchives = ($ArchiveFolder + "future\*" + $ArchiveFileExtenstion)
$FutureArchiveAnnotations = ($FutureArchives + ".ann")
# Event queues
$Queues = ($QueueFolder + "*.dat")
$ExclusionPaths = @(
$Logs,
$Archives,
$ArchiveAnnotations,
$FutureArchives,
$FutureArchiveAnnotations,
$Queues
)
# Loop through adding the paths as
ForEach($ExclusionPath in $ExclusionPaths)
{
Add-MpPreference -ExclusionPath $ExclusionPath -Verbose
}
Configuration EnableVBS
{
param(
[String]$NodeName="localhost"
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Node $NodeName
{
#region Windows Defender
# Enable Device Guard
# https://docs.microsoft.com/en-us/windows/device-security/device-guard/deploy-device-guard-enable-virtualization-based-security
$DGRegistryKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard'
Registry "$DGRegistryKey\EnableVirtualizationBasedSecurity"
{
Ensure = 'Present'
Key = $DGRegistryKey
ValueName = 'EnableVirtualizationBasedSecurity'
ValueData = 1
ValueType = 'DWORD'
}
Registry "$DGRegistryKey\RequirePlatformSecurityFeatures"
{
Ensure = 'Present'
Key = $DGRegistryKey
ValueName = 'RequirePlatformSecurityFeatures'
ValueData = 1
ValueType = 'DWORD'
}
Registry "$DGRegistryKey\Locked"
{
Ensure = 'Present'
Key = $DGRegistryKey
ValueName = 'Locked'
ValueData = 0
ValueType = 'DWORD'
}
# Enable application control
$HVCIRegistryKey = $DGRegistryKey + '\Scenarios\HypervisorEnforcedCodeIntegrity'
Registry "$HVCIRegistryKey\Enabled"
{
Ensure = 'Present'
Key = $HVCIRegistryKey
ValueName = 'Enabled'
ValueData = 1
ValueType = 'DWORD'
}
Registry "$HVCIRegistryKey\Locked"
{
Ensure = 'Present'
Key = $HVCIRegistryKey
ValueName = 'Locked'
ValueData = 0
ValueType = 'DWORD'
}
# Enable Credential Guard
# https://docs.microsoft.com/en-us/windows/access-protection/credential-guard/credential-guard-manage
$LSARegistryKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\LSA'
Registry "$LSARegistryKey\LsaCfgFlags"
{
Ensure = 'Present'
Key = $LSARegistryKey
ValueName = 'LsaCfgFlags'
ValueData = 1
ValueType = 'DWORD'
}
}
}
EnableVBS
# https://blogs.technet.microsoft.com/datacentersecurity/2016/09/20/overview-of-device-guard-in-windows-server-2016/
$policyBase = $($home + '\Documents\PublisherSiPolicy.xml')
$policyMS = $($home + '\Documents\MSBlockSiPolicy.xml')
$policyConfig = $($home + '\Documents\SiPolicy.xml')
$policyBin = $($home + '\Documents\SiPolicy.bin')
$policyP7B = $($env:WinDir + '\System32\CodeIntegrity\SiPolicy.p7b')
<#
# The base policy is generated with New-CIPolicy
# This command takes significant time to scan the machine
# We will leverage an existing file for the live demo
New-CIPolicy -Level Publisher -Fallback Hash -UserPEs -FilePath $policyBase
#>
# Merge in Microsoft recommended policy to reduce available bypasses
# https://docs.microsoft.com/en-us/windows/security/threat-protection/device-guard/steps-to-deploy-windows-defender-application-control
Merge-CIPolicy -PolicyPaths $policyBase, $policyMS -OutputFilePath $policyConfig
# Alter Policy to Enforce
Set-RuleOption -FilePath $policyConfig -Option 3 -delete
# Convert the CI Policy to the binary format and deploy it
ConvertFrom-CIPolicy $policyConfig $policyBin
Copy-Item $policyBin $policyP7B -Verbose
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment