PrintNightmare – CVE-2021-34528

Microsoft a publié une vulnérabilité dans le service spouleur d’impression, dont tous les détails peuvent se retrouver à ce lien. Il est important de prendre cette faille au sérieux car elle permet notamment d’exécuter du code en tant qu’utilisateur SYSTEM ce qui peut être désastreux en fonction de la machine sur laquelle l’exécution est réalisée.

Plusieurs workarounds sont proposés par Microsoft, et ayant mis en place les corrections nécessaires, je vais partager les quelques scripts (Powershell 5.1) que j’ai conçu afin de me faciliter la tâche.

Tout d’abord, à partir d’un fichier CSV d’inventaire, ce script va interroger le système distant pour récupérer l’état complet du service Spooler (état actuel, type de démarrage, etc.). Ainsi, il est possible d’avoir un état des lieux sur l’exécution de ce service.

$inv = Import-Csv inventaire.csv
$output = @()
foreach($srv in $inv){
    Write-Output $srv.hostname
    $output+=Get-Service -Name Spooler -Computer $srv.hostname
}
$output | Export-Csv spooler_status.csv -Delimiter ";" -Encoding UTF8

Ce deuxième script va appliquer le workaround conseillé par Microsoft ; il s’agit de l’extinction du service Spooler et la désactivation du démarrage automatique de celui-ci. Le script interroge ensuite de nouveau le service pour obtenir son état et génère un fichier CSV de retour.

$inv = Import-Csv spooler_off.csv -Delimiter ";"
$output = @()
foreach($srv in $inv){
    Write-Host $srv.MachineName
    Stop-Service -InputObject $(Get-Service -Name Spooler -ComputerName $srv.MachineName)
    Set-Service -Name Spooler -StartupType Disabled -ComputerName $srv.MachineName
    $srvstat = New-Object PSCustomObject
    $srvstat | Add-Member -Name "Hostname" -Value $srv.MachineName -MemberType NoteProperty
    $srvstat | Add-Member -Name "SpoolerStatus" -Value (Get-Service -Name Spooler -ComputerName $srv.MachineName).Status -MemberType NoteProperty
    $output+=$srvstat
}
$output | Export-Csv spooler_processed.csv -Encoding UTF8 -Delimiter ";"

Et enfin, puisque Microsoft a mis à jour aujourd’hui la vulnérabilité en incluant un contrôle de clefs de registre qui n’était pas présent hier, ce troisième script va interroger les serveurs d’une liste au format CSV afin de récupérer l’état des clefs de registre mettant à risque le système.

$output = @()
$inv = Import-Csv inventaire_hostname.csv -Delimiter ";"
foreach($srv in $inv){
    Write-Host $srv.Hostname
    $pnp = New-Object PSCustomObject
    if(Invoke-Command -ComputerName $srv.Hostname { Test-Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint\" }){
        $pnp | Add-Member -Name "Hostname" -Value $srv.Hostname -MemberType NoteProperty
        $pnp | Add-Member -Name "PointAndPrintKeyExists" -Value "Yes" -MemberType NoteProperty  
        if((Invoke-Command -ComputerName $srv.Hostname { Get-ItemPropertyValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint\" -Name NoWarningNoElevationOnInstall }) -eq 1) {
            $pnp | Add-Member -Name "NoWarningNoElevationOnInstall" -Value "1" -MemberType NoteProperty
        }
        else { $pnp | Add-Member -Name "NoWarningNoElevationOnInstall" -Value "0" -MemberType NoteProperty }
        if((Invoke-Command -ComputerName $srv.Hostname {Get-ItemPropertyValue "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint\" -Name NoWarningNoElevationOnUpdate }) -eq 1) {
            $pnp | Add-Member -Name "NoWarningNoElevationOnUpdate" -Value "1" -MemberType NoteProperty
        }
        else { $pnp | Add-Member -Name "NoWarningNoElevationOnUpdate" -Value "0" -MemberType NoteProperty }
    }
    else {$pnp | Add-Member -Name "Hostname" -Value $srv.Hostname -MemberType NoteProperty ; $pnp | Add-Member -Name "PointAndPrintKeyExists" -Value "No or Server Unreacheable" -MemberType NoteProperty }
    $output+=$pnp
}
$output | Export-Csv pointnprint.csv -Delimiter ";" -Encoding UTF8 

En fonction du nombre de serveurs à interroger, l’exécution du script peut être relativement longue. Par souci de rapidité de développement, je n’ai pas inclus de contrôle de réponse du serveur avant d’utiliser Invoke-Command. Il est donc possible que Powershell retourne une erreur si le serveur ne répond pas à la requête ou si il la rejette, cela n’impacte pas le bon déroulement du script.

Naturellement, une étude d’impact et de faisabilité est à faire avant toute intervention et extinction d’un service, même pour le spouleur qui n’est pas d’une importance capitale sur une grande majorité de machines. Il ne reste donc plus qu’à attendre l’application des correctifs de sécurité publiés par Microsoft.