Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
The detailed steps I took to develop a locked down code integrity policy on my Surface Pro 4
#######################################
# Device Guard configuration phase #1 #
# Deny-all audit policy deployment #
#######################################
# The staging directory I'm using for my Device Guard setup
$PolicyDirectory = 'C:\DGPolicyFiles'
# Path to the empty template policy that will place Device Guard
# into audit mode and simulate denying execution of everything.
$EmptyPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyPolicy.xml'
# Generate an empty, deny-all policy
# There is no intuitive way to generate an empty policy so we will
# go about doing it by generating a policy based on an empty directory.
$EmptyDir = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyDir'
mkdir -Path $EmptyDir
New-CIPolicy -FilePath $EmptyPolicyXml -Level PcaCertificate -ScanPath $EmptyDir -NoShadowCopy
Remove-Item $EmptyDir
# Only load drivers that are WHQL signed
Set-RuleOption -FilePath $EmptyPolicyXml -Option 2
# Enable UMCI enforcement
Set-RuleOption -FilePath $EmptyPolicyXml -Option 0
# Only allow drivers to load that are WHQL signed by trusted MS partners
# who sign their drivers with an extended validation certificate.
# Note: this is an idealistic setting that will probably prevent some of your
# drivers from loading. Enforcing this in audit mode however will at least
# inform you as to what the problematic drivers are.
Set-RuleOption -FilePath $EmptyPolicyXml -Option 8
# A generated policy will also have the following policy options set by default
# * Unsigned System Integrity Policy
# * Audit Mode
# * Advanced Boot Options Menu
# * Enforce Store Applications
# In order to deploy the policy, the XML policy has to be converted
# to p7b format with the ConvertFrom-CIPolicy cmdlet.
$EmptyPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'EmptyPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $EmptyPolicyXml -BinaryFilePath $EmptyPolicyBin
# We're going to copy the policy file in binary format to here. By simply copying
# the policy file to this destination, we're deploying our policy and enabling it
# upon reboot.
$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'
Copy-Item -Path $EmptyPolicyBin -Destination $CIPolicyDeployPath -Force
# At this point, you may want to clear the Microsoft-Windows-CodeIntegrity/Operational
# event log and increase the size of the log to accommodate the large amount of
# entries that will populate the event log as a result of an event log entry being
# created upon code being loaded.
# Optional: Clear Device Guard related logs
# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational
# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"
# Reboot the computer and the deny-all audit policy will be in place.
######################################################
# Device Guard configuration phase #2 #
# Code integrity policy creation based on audit logs #
######################################################
# Hopefully, you've spent a few days using your system for its intended purpose and didn't
# install any software that would compromise the "gold image" that you're aiming for.
# Now we're going to craft a CI policy based on what would have been denied from loading.
# Obviously, these are the kinds of applications, scripts, and drivers that will need to
# execute in order for your system to work as intended.
# The staging directory I'm using for my Device Guard setup
$PolicyDirectory = 'C:\DGPolicyFiles'
# Path to the CI policy that will be generated based on the entries present
# in the CodeIntegrity event log.
$AuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.xml'
# Generate the CI policy based on what would have been denied in the event logs
# (i.e. Microsoft-Windows-CodeIntegrity/Operational and Microsoft-Windows-AppLocker/MSI and Script)
# PcaCertificate is probably the best file rule level for this round of CI policy generation
# as it is the highest in the code signing cert signer chain and it has a longer validity time frame
# than a leaf certificate (i.e. lowest in the signing chain).
# This may take a few minutes to generate the policy.
# The resulting policy will result in a rather concise list of whitelisted PCA certificate signers.
New-CIPolicy -FilePath $AuditPolicyXml -Level PcaCertificate -Audit -UserPEs
# Note: This policy, when deployed will still remain in audit mode as we should not be confident
# at this point that we've gotten everything right.
# Now let's deploy the new policy
$AuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $AuditPolicyXml -BinaryFilePath $AuditPolicyBin
# We're going to copy the policy file in binary format to here. By simply copying
# the policy file to this destination, we're deploying our policy and enabling it
# upon reboot.
$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'
Copy-Item -Path $AuditPolicyBin -Destination $CIPolicyDeployPath -Force
# Optional: Clear Device Guard related logs
# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational
# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"
# Reboot the computer and the audit policy will be in place.
################################################################
# Device Guard configuration phase #3 #
# Code integrity policy final tweaks while still in audit mode #
################################################################
# My goal in this phase is to see what remaining CodeItegrity log entries
# exist and to try to rectify them while still in audit mode before placing
# code integrity into enforcement mode.
# For me, I had about 30 event log entries that indicated the following:
#
# Code Integrity determined that a process (Winload) attempted to load
# System32\Drivers\mup.sys that did not meet the Authenticode signing
# level requirements or violated code integrity policy. However, due to
# code integrity auditing policy, the image was allowed to load.
# Upon trying to create a new policy based on these event log entries via the following command
# New-CIPolicy -FilePath Audit2.xml -Level PcaCertificate -Audit
# I got a bunch of the following warnings:
#
# File at path \\?\GLOBALROOTSystem32\Drivers\Wof.sys in the audit log was not found.
# It has likely been deleted since it was last run
#
# Ugh. No it wasn't deleted. This looks like a path parsing bug. Personally, I'm
# comfortable trusting all drivers in %SystemRoot%\System32\Drivers so I'm going
# to create a policy from that directory and merge it with my prior. Afterall,
# my system would not boot if I didn't whitelist them.
$PolicyDirectory = 'C:\DGPolicyFiles'
# Path to the CI policy that will be generated based on the entries present
# in the CodeIntegrity event log.
$DriverPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'SystemDriversPolicy.xml'
# Create a whitelisted policy for all drivers in System32\drivers to account for
# the New-CIPolicy audit log scanning path parsing bug...
# Note: this really annoying bug prevented and rules in the previous phase from being created
# for drivers - only user-mode binaries and scripts. If I were to deploy and enforce a policy without
# driver whitelist rules, I'd have an unbootable system.
New-CIPolicy -FilePath $DriverPolicyXml -Level PcaCertificate -ScanPath 'C:\Windows\System32\drivers\'
# Some may consider this strategy to be too permissive (myself partially included). The ideal strategy
# here probably would have been to pull out the individual driver paths, copy them to a dedicated
# directory and generate a policy for just those drivers. For the ultra paranoid, this is left as an
# exercise to the reader.
# Now we have to merge this policy with the last one as a means of consolidating whitelist rules.
$AuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'AuditLogPolicy.xml'
$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'
Merge-CIPolicy -OutputFilePath $MergedAuditPolicyXml -PolicyPaths $DriverPolicyXml, $AuditPolicyXml
# Now let's deploy the new policy
$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin
# We're going to copy the policy file in binary format to here. By simply copying
# the policy file to this destination, we're deploying our policy and enabling it
# upon reboot.
$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'
Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force
# Optional: Clear Device Guard related logs
# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational
# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"
# Reboot the computer and the merged policy will be in place.
###################################################
# Device Guard configuration phase #4 #
# Deployment of the CI policy in enforcement mode #
###################################################
# This is the point where I feel comfortable enforcing my policy. The CodeIntegrity log
# is now only populated with a few anomalies - e.g. primarily entries related to NGEN
# native image generation. I'm okay with blocking these but hopefully, the Device Guard
# team can address how to handle NGEN generated images properly since this is not documented.
$PolicyDirectory = 'C:\DGPolicyFiles'
$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'
# Now all we need to do is remove audit mode from the policy, redeploy, reboot, and cross our
# fingers that the system is useable. Note that the "Advanced Boot Options Menu" option is still
# enabled so we have a way to delete the deployed policy from a recovery console if things break.
Set-RuleOption -FilePath $MergedAuditPolicyXml -Delete -Option 3
$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin
$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'
Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force
# Optional: Clear Device Guard related logs
# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational
# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"
# Reboot the computer and the enforced policy will be in place. This is the moment of truth!
###################################################
# Device Guard configuration phase #5 #
# Updating policy to no longer enforce EV signers #
###################################################
# So it turns out that I was a little overambitious in forcing EV signer enforcement
# on my Surface Tablet as pretty much all of my Surface hardware drivers didn't load.
# This is kind of a shame considering I would expect MS hardware drivers to be held to
# the highest standards imposed by MS. So I'm going to remove EV signer enforcement and
# while I'm at it, I'm going to enforce blocking of flight-signed drivers. These are drivers
# signed by an MS test certificate used in Windows Insider Preview builds. So obviously, you
# won't want to be running WIP builds of Windows if you're enforcing this.
# FYI, I was fortunate enough for the system to boot to discover that EV signing was the issue.
$PolicyDirectory = 'C:\DGPolicyFiles'
$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'
# No longer enforce EV signers
Set-RuleOption -FilePath $MergedAuditPolicyXml -Delete -Option 8
# Enforce blocking of flight signed code.
Set-RuleOption -FilePath $MergedAuditPolicyXml -Option 4
$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin
$CIPolicyDeployPath = Join-Path -Path $env:SystemRoot -ChildPath 'System32\CodeIntegrity\SIPolicy.p7b'
Copy-Item -Path $MergedAuditPolicyBin -Destination $CIPolicyDeployPath -Force
# Optional: Clear Device Guard related logs
# wevtutil clear-log Microsoft-Windows-CodeIntegrity/Operational
# wevtutil clear-log "Microsoft-Windows-AppLocker/MSI and Script"
# Reboot the computer and the modified, enforced policy will be in place.
# BTW in retrospect, it would have been smart to have enabled "Boot Audit on Failure"
# with Set-RuleOption as it would have placed device guard into audit mode in order to allow
# boot drivers to boot that would have otherwise been blocked by policy.
###################################################
# Device Guard configuration phase #6 #
# Monitoring and continued hardening #
###################################################
<#
At this point you have a decent starting point and I'll leave it up to you as
to how you'd like to proceed in terms of CI policy configuration and deployment.
Me personally, I performed the following:
1) Used Add-SignerRule to add an Update and User signer rule with my personal code signing certificate.
This grants me permission to sign my policy and execute user-mode binaries and PowerShell scripts
signed by me. I need to sign some of my PowerShell code that I use often since it is incompatible
in constrained language mode. Signed scripts authorized by CI policy execute in full language mode.
Obviously, I personally need to sign my own code sparingly. For example, it would be dumb for me to
sign Invoke-Shellcode since that would explicitly circumvent user-mode code integrity.
2) Remove "Unsigned System Integrity Policy" from the configuration. This forces me to sign the policy.
It also prevents modification and removal of a deployed policy and it can only be updated by signing
an updated policy.
3) I removed the "Boot Menu Protection" option from the config. This is a potential vulnerability to an
attacker with physical access.
4) I also enabled virtualization-based security via group policy to achieve the hardware supported
Device Guard enforcements.
#>
# Code I used to allow my code signing cert to sign the policy and sign user-mode binaries.
# I don't plan on using my code signing cert to sign drivers so I won't allow that right now.
# Note: I'm peforming these steps on an isolated system that contains my imported code signing
# certificate. I don't have my code signing cert on the system that I'm protecting with
# Device Guard hopefully for obvious reasons.
$PolicyDirectory = 'C:\DGPolicyFiles'
$CodeSigningSertPath = Join-Path $PolicyDirectory 'codesigning.cer'
$MergedAuditPolicyXml = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.xml'
Add-SignerRule -FilePath $MergedAuditPolicyXml -CertificatePath $CodeSigningSertPath -User -Update
$MergedAuditPolicyBin = Join-Path -Path $PolicyDirectory -ChildPath 'MergedAuditPolicy.bin'
ConvertFrom-CIPolicy -XmlFilePath $MergedAuditPolicyXml -BinaryFilePath $MergedAuditPolicyBin
# I'm signing my code integrity policy now.
signtool.exe sign -v /n "Matthew Graeber" -p7 . -p7co 1.3.6.1.4.1.311.79.1 -fd sha256 $MergedAuditPolicyBin
# Now, once I deploy this policy, I will only be able to make updates to the policy by
# signing an updated policy with the same signing certificate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.