Validating PrintNightmare Remediation with PowerShell
Posted on July 11, 2021
- and tagged as
- powershell,
- security,
- windows
There has been a lot of uncertainty around CVE-2021-34527, dubbed ‘PrintNightmare’. From whether the released out-of-band patches work, to GPO settings & associated registry values which allow the mitigation in the patch to be bypassed, and arguments regarding whether these GPO settings are a feature or a security vulnerability that needs to be patched/removed.
Further complicating matters is that the vulnerability is exploitable against both local systems for privilege escalation, and remote systems, depending on various configuration elements.
Will Dormann has been neck deep in this and has released a flowchart showing various paths to exploitation and remediation.
I've made a few changes to this flowchart to make it more clear, and also add some more boxes. Because of course.
— Will Dormann (@wdormann) July 8, 2021
I also added it to the CERT/CC vulnerability note for #PrintNightmare VU#383432 CVE-2021-34527https://t.co/c7Durfn4BL pic.twitter.com/CfLcEe7mgf
I’ve taken this and turned it into a PowerShell function that can be run on individual systems to determine their posture and help validate protection efforts.
One thing of note is that the function doesn’t differentiate between local and remote exportability. It’s also worth mentioning that future cumulative updates will include the relevant fixes, the function below specifically looks for either the out-of-band (released 6 July 2021) patches or the regular July 2021 cumulative/security patches. If you’re reading this in the future you will need to update the patch detection code.
Here is the function, with some examples below.
16/07/2021 Update: Added in-band July patches which include the CVE-2021-34527 fixes.
function PNValidate {
$Results = [PSCustomObject]@{
Spooler = $null
PatchInstalled = $false
RestrictDriverInstallationToAdministrators = $null
NoWarningNoElevationOnInstall = $null
UpdatePromptSettings = $null
Exploitable = $true
Explanation = $null
}
# Check spooler status
$Spooler = (Get-Service Spooler -ErrorAction SilentlyContinue).Status
if (($null -eq $Spooler) -or ($Spooler -ne "Running")) {
$Results.Spooler = "Secure"
}
else {
$Results.Spooler = "Insecure"
}
# Check patch installation status
$Patches = @("KB5004954", "KB5004958", "KB5004956", "KB5004960", "KB5004953", "KB5004951", "KB5004955", "KB5004959", "KB5004948", `
"KB5004950", "KB5004945", "KB5004946", "KB5004947", "KB5004249", "KB5004238", "KB5004244", "KB5004245", "KB5004237", `
"KB5004289", "KB5004307", "KB5004298", "KB5004285", "KB5004305", "KB5004299", "KB5004294", "KB5004302")
$InstalledPatches = (Get-HotFix).HotFixID
$Patches | % { if ($InstalledPatches -contains $_) { $Results.PatchInstalled = $true } }
# Check registry keys
# RestrictDriverInstallationToAdministrators
$RestrictDriverInstallationToAdministrators = (Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint" -ErrorAction SilentlyContinue).RestrictDriverInstallationToAdministrators
if (($RestrictDriverInstallationToAdministrators -eq $null) -or ($RestrictDriverInstallationToAdministrators -ne 1)) {
$Results.RestrictDriverInstallationToAdministrators = "Insecure"
}
else {
$Results.RestrictDriverInstallationToAdministrators = "Secure"
}
# NoWarningNoElevationOnInstall
$NoWarningNoElevationOnInstall = (Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint" -ErrorAction SilentlyContinue).NoWarningNoElevationOnInstall
if (($NoWarningNoElevationOnInstall -eq $null) -or ($NoWarningNoElevationOnInstall -eq 0)) {
$Results.NoWarningNoElevationOnInstall = "Secure"
}
else {
$Results.NoWarningNoElevationOnInstall = "Insecure"
}
# UpdatePromptSettings
$UpdatePromptSettings = (Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint" -ErrorAction SilentlyContinue).UpdatePromptSettings
if (($UpdatePromptSettings -eq $null) -or ($UpdatePromptSettings -eq 0)) {
$Results.UpdatePromptSettings = "Secure"
}
else {
$Results.UpdatePromptSettings = "Insecure"
}
# Validate results
if ($Results.Spooler -eq "Secure") {
$Results.Exploitable = $false
$Results.Explanation = "Not exploitable as spooler service is not running"
}
elseif (($Results.PatchInstalled -eq $true) -and ($Results.RestrictDriverInstallationToAdministrators -eq "Secure")) {
$Results.Exploitable = $false
$Results.Explanation = "Not exploitable as patch is installed and RestrictDriverInstallationToAdministrators is set to secure value"
}
else {
if ($Results.PatchInstalled -eq $true) {
if ($Results.NoWarningNoElevationOnInstall -eq "Insecure") {
$Results.Explanation = "Exploitable as NoWarningNoElevationOnInstall is set to insecure value"
}
elseif (($Results.NoWarningNoElevationOnInstall -eq "Secure") -and ($Results.UpdatePromptSettings -eq "Secure")) {
$Results.Exploitable = $false
$Results.Explanation = "Not exploitable as patch is installed and the registry settings NoWarningNoElevationOnInstall and UpdatePromptSettings are both set to secure values"
}
elseif (($Results.NoWarningNoElevationOnInstall -eq "Secure") -and ($Results.UpdatePromptSettings -eq "Insecure")) {
$Results.Explanation = "Exploitable as UpdatePromptSettings is set to insecure value"
}
}
else {
$Results.Explanation = "Exploitable as patch is not installed"
}
}
$Results
}
Examples
Spooler service stopped
PS C:\> Get-Service spooler; PNValidate
Status Name DisplayName
------ ---- -----------
Stopped spooler Print Spooler
Spooler : Secure
PatchInstalled : False
RestrictDriverInstallationToAdministrators : Insecure
NoWarningNoElevationOnInstall : Secure
UpdatePromptSettings : Secure
Exploitable : False
Explanation : Not exploitable as spooler service is not running
Unpatched system with the spooler service running
PS C:\> PNValidate
Spooler : Insecure
PatchInstalled : False
RestrictDriverInstallationToAdministrators : Insecure
NoWarningNoElevationOnInstall : Secure
UpdatePromptSettings : Secure
Exploitable : True
Explanation : Exploitable as patch is not installed
Patch installed with no vulnerable GPO/registry settings
PS C:\> PNValidate
Spooler : Insecure
PatchInstalled : True
RestrictDriverInstallationToAdministrators : Insecure
NoWarningNoElevationOnInstall : Secure
UpdatePromptSettings : Secure
Exploitable : False
Explanation : Not exploitable as patch is installed and the registry settings NoWarningNoElevationOnInstall and UpdatePromptSettings are both set to secure values
Validating remote machines
PS C:\> Invoke-Command -ComputerName "srv-lab1","srv-lab2","srv-lab3" -ScriptBlock ${Function:PNValidate} | select PSComputerName, Exploitable
PSComputerName Exploitable
-------------- ----------
srv-lab1 False
srv-lab2 True
srv-lab3 False
Latest copy of the function with any changes will be available on GitHub.