Bluekeep, why would you still be vulnerable ? SHA2 signing
Bluekeep, why would you still be vulnerable? SHA2 signing.
Patch management is a pain, and the more obsolete the OS, the trickier it becomes. Windows 7 and 2008 R2 (Windows 6.1) are due to reach End Of Life (EOL) on 14 January 2020 (tomorrow at the time of this writing) but your computers may not have been patched since August 2019 if you haven’t been careful.
Indeed, August 2019 was the last month where Microsoft KB were signed using SHA1. Since then, KBs are only signed using SHA2, but this hash function must be installed on Windows 6.1 in order for your computers and servers to install them. Otherwise they will fail with error code 0x80092004 (CRYPT_E_NOT_FOUND).
Since Windows Server 2012R2, this hash function is already included so there is nothing to worry about for 2012R2, 2016 and 2019.
Unfortunately, in August and September 2019, Bluekeep, a critical security vulnerability on Microsoft’s Remote Desktop Protocol, which allow s for the possibility of remote code execution as system, was released, and patched by Microsoft during those two months. Therefore, if some computers running Windows 6.1 have not installed the SHA2 hash function before Septembre 2019, they have not been patched since then and are vulnerable to Bluekeep. I hope it’s not your DC, or your WSUS…
But don’t panic, here is what you can do :
- Find the machines in error
- Fix the machines in error
Finding the machines in error :
If you’re using WSUS to patch your domain, I got something for you. Since WSUS Report Viewer is awfully slow and a pain to use, I developed a quick script using Powershell to retrieve every computer in error.
This script must be run as Administrator on the WSUS server delivering the patch to the clients. It was only tested on WSUS running on at least Windows 2012R2. The script simply looks for computers in error according to wsus reports and writes their names, the error codes and the KBs in failure in a CSV file.
SCCM is out of scope in this article but aleardy provides the necessary dashboard if you’re patching with it. It can be used alongside WSUS to quickly deploy a KB on you infrastructure.
Here is a quick usage, followed by a possible result and the script itself. You can also download it.
1.\wsus_computers_in_error.ps1 –ServerName wsus.ipfyx.fr –ServerPort 8531 –RelativeTime -168 –ErrorCode 0x80092004 –CsvPath ".\computer_no_sha2_$(Get-Date –Format yyyy-MM-dd).csv"
1<#
2 .SYNOPSIS
3 Retrieve every computer in error when installing a Microsoft KB, with a specifi error code or not (SHA2 error : 0x80092004)
4
5 .PARAMETER ErrorCode
6 Error code
7
8 .PARAMETER ServerName
9 Name of the WSUS Server
10
11 .PARAMETER ServerPort
12 Port of the WSUS Server (8531 if it is using SSL, else 8530)
13
14 .PARAMETER RelativeTime
15 Relative time between now and a past time (ex : -24 is -24h)
16
17 .PARAMETER CsvPath
18 Output file in CSV
19
20 .NOTES
21 (c) ipfyx 2019
22 version 1.5
23
24 Example
25 .\wsus_computers_in_error.ps1 -ServerName <Server> -ServerPort 8531 -RelativeTime -168 -Errorcode 0x80092004 -CsvPath ".\computer_no_sha2_$(Get-Date -Format yyyy-MM-dd).csv"
26
27#>
28
29# This script must be run with Administrator privileges.
30#Requires -RunAsAdministrator
31
32Param([Parameter(Mandatory=$True)][string]$ServerName, [Parameter(Mandatory=$True)][string]$ServerPort,
33[Parameter(Mandatory=$False)][int32]$RelativeTime=-48, [Parameter(Mandatory=$False)][string]$Errorcode='',
34[Parameter(Mandatory=$True)][string]$CsvPath)
35
36Function Get_Computer_Scope {
37 [CmdletBinding()]
38 Param(
39 [Parameter(Mandatory=$True)]
40 $Wsus,
41
42 [Parameter(Mandatory=$False)]
43 [string]
44 $InstallationStates = 'Failed',
45
46 [Parameter(Mandatory=$False)]
47 [boolean]
48 $IncludeDownstreamComputerTargets = $true
49 )
50
51 # Retrieve every computer in error according to WSUS reports
52 $computerScope = new-object Microsoft.UpdateServices.Administration.ComputerTargetScope
53 $computerScope.IncludedInstallationStates = $InstallationStates
54
55 # Retrieve every compuer in error accoring to downtream servers too
56 $computerScope.IncludeDownstreamComputerTargets = $InstallationStates
57
58 $Wsus.GetComputerTargets($computerScope)
59}
60
61Function Get_Event_History {
62 [CmdletBinding()]
63 Param(
64 [Parameter(Mandatory=$True)]
65 $Wsus,
66
67 [Parameter(Mandatory=$True)]
68 [string]
69 $RelativeTime,
70
71 [Parameter(Mandatory=$False)]
72 [string]
73 $InstallationStates = 'Failed',
74
75 [Parameter(Mandatory=$False)]
76 [string]
77 $ErrorCode = ''
78 )
79
80
81 # Quelques messages d'erreur sont juste "Installation Failure: Windows failed to install the following update with error %1: %2"
82 # Et l'updateID associé n'existe pas dans la bdd (2ce8a11d-7f90-4489-92f3-a202585b793b), pourquoi, je sais pas, mais ca fout le sbeul
83
84 # Retrieve every computer in error, no matter the error code, if the error code is not spectified
85 if ($ErrorCode -eq '') {
86
87 $Wsus.GetUpdateEventHistory("$((Get-Date).AddHours($RelativeTime))","$(Get-Date)")|
88 Where-Object {$_.Status -eq $InstallationStates}|
89 Select-Object ComputerId, CreationDate, Message, ErrorCode, UpdateId
90 }
91 else {
92 $Wsus.GetUpdateEventHistory("$((Get-Date).AddHours($RelativeTime))","$(Get-Date)")|
93 Where-Object {$_.Status -eq $InstallationStates}|
94 Where-Object {$_.ErrorCode -eq $Errorcode} |
95 Select-Object ComputerId, CreationDate, Message, ErrorCode, UpdateId
96 }
97}
98
99
100If ($ServerPort -eq '8531') {
101 $Wsus = Get-WsusServer -Name $ServerName -PortNumber $ServerPort -UseSsl
102} Else {
103 $Wsus = Get-WsusServer -Name $ServerName -PortNumber $ServerPort
104}
105
106$ComputersInFailure = Get_Computer_Scope -Wsus $Wsus
107
108$ComputersAffected = @{}
109
110$ErrorMessages = Get_Event_History -RelativeTime $RelativeTime -ErrorCode $ErrorCode -Wsus $Wsus
111$ErrorMessages | ForEach-Object {
112
113 $ComputerId = $_.ComputerId
114 $ComputerWithError = $ComputersInFailure | Where-Object {$_.id -eq $ComputerId}
115
116 if($ComputerWithError -ne $null) {
117
118 try {
119 # Retrieve the Knowledge Base number associated with the update guid
120 $updateGuid = $_.UpdateId.UpdateId.guid
121 $updateId = New-Object Microsoft.UpdateServices.Administration.UpdateRevisionId($updateGuid)
122 $update = $wsus.GetUpdate($updateId)
123
124 # A computer could fail multiple KB install so the key to distinguish every case is FullDomaineName+KB
125 $Key = $ComputerWithError.FullDomainName+[System.Convert]::ToString($_.ErrorCode,16)+$update.KnowledgebaseArticles[0]
126
127 if ($ComputersAffected.ContainsKey($Key)) {
128
129 # We only want the last report in error
130 if ($ComputerWithError.LastReportedStatusTime -gt $ComputersAffected[$Key].LastReportedStatusTime) {
131
132 $ComputersAffected[$Key]=
133 [pscustomobject]@{
134 FullDomaineName = $ComputerWithError.FullDomainName
135 IPAddress = $ComputerWithError.IPAddress
136 OSDescription = $ComputerWithError.OSDescription
137 ErrorCode = [System.Convert]::ToString($_.ErrorCode,16)
138 LastReportedStatusTime = $ComputerWithError.LastReportedStatusTime
139 UpdateTitle = $update.Title
140 KB = $update.KnowledgebaseArticles[0]
141 ArrivalDate = $update.ArrivalDate
142
143 }
144 }
145
146 } else {
147
148 $ComputersAffected[$Key]=
149 [pscustomobject]@{
150 FullDomaineName = $ComputerWithError.FullDomainName
151 IPAddress = $ComputerWithError.IPAddress
152 OSDescription = $ComputerWithError.OSDescription
153 ErrorCode = [System.Convert]::ToString($_.ErrorCode,16)
154 LastReportedStatusTime = $ComputerWithError.LastReportedStatusTime
155 UpdateTitle = $update.Title
156 KB = $update.KnowledgebaseArticles[0]
157 ArrivalDate = $update.ArrivalDate
158
159 }
160 }
161
162 }
163 catch [Microsoft.UpdateServices.Administration.WsusObjectNotFoundException]{
164 # Some "ghost" reports can mess up the script
165 # We don't do anything
166 }
167 }
168
169}
170
171$ComputersAffected.Values | Export-Csv -Encoding UTF8 -Path $CsvPath
Here is a possible output :
1#TYPE System.Management.Automation.PSCustomObject
2"FullDomaineName","IPAddress","OSDescription","ErrorCode","LastReportedStatusTime","UpdateTitle","KB","ArrivalDate"
3"toto.ipfyx.fr","1.1.1.1","Windows 7","80092004","01/01/1970 00:00:00","Cumulative Security Update","4474333","01/01/1970 01:00:00"
Fixing the machines in error :
Since Windows 6.1 is due to reach End Of Life (EOL) on 14 January 2020, so why bother ? Well, Bluekeep is such a critical vulnerability that you should not neglect it. After that, you should hurry to upgrade your server to at least Windows 2012 R2, and you computers to Windows 10.
In the meantime, to fix this issue, you should install :
- For Windows 2008R2 et Windows 7 :
- KB 4474419 (cumulative security update from July 2019 or before, last KB signed using SHA1 for Windows 2008R2)
- KB 4490628 (Stack update from March 2019)
- For Windows 2008 :
- KB 4474419 (cumulative security update from June 2019, last KB signed using SHA1 for Windows 2008)
- KB 4493730 (Stack update from April 2019)
Quick bonus :
If you don’t specify any error code to the script, it will return every computer in error, whatever the error code. You can therefore use this script to diagnose your patch management. The CSV result could, for example, be put in splunk to build dashboard.
1.\wsus_computers_in_error.ps1 –ServerName wsus.ipfyx.fr –ServerPort 8531 –RelativeTime -168 –CsvPath ".\computer_in_error_$(Get-Date –Format yyyy-MM-dd).csv"
I wish you good luck ! Any feedback will of course be greatly appreciated.