Validating PrintNightmare Remediation with PowerShell

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 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.


If you enjoyed this post consider sharing it on , , , or , and .