Create a gist now

Instantly share code, notes, and snippets.

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