icetray - Fotolia
Using wsusscn2.cab to find missing Windows updates
Avoid unnecessary security headaches by using the Windows Update offline scan file and PowerShell to ensure your systems have all their Microsoft patches.
Keeping your Windows Server and Windows desktop systems updated can be tricky, and finding missing patches in conventional ways might not be reliable.
There are a few reasons why important security patches might not get installed. They could be mistakenly declined in Windows Server Update Services or get overlooked in environments that a lack an internet connection.
Microsoft provides a Windows Update offline scan file, also known as wsusscn2.cab, to help you check Windows systems for missing updates. The CAB file contains information about most patches for Windows and Microsoft applications distributed through Windows Update.
The challenge with the wsusscn2.cab file is its size. It weighs in around 650 MB, and distributing it to all the servers to perform a scan can be tricky and time-consuming. This tutorial explains how to avoid those issues and run it on all of your servers in a secure and timely manner using IIS for file transfer instead of SMB or PowerShell sessions.
Requirements for offline scanning
There are some simple requirements to use this tutorial:
- a server or PC running Windows Server 2012 or newer or Windows 10;
- a domain account with local administrator on the servers you want to scan; and
- PowerShell remoting enabled on the servers you want to scan.
Step 1. Install IIS
First, we need a web server we can use to distribute the wsusscn2.cab file. There are several ways to copy the file, but they all have different drawbacks.
For example, we could distribute the wsusscn2.cab file with a regular file share, but that requires a double-hop. You could also copy the wsusscn2.cab file over a PowerShell session, but that causes a lot of overhead and is extremely slow for large files. An easier and more secure way to distribute the file is through HTTP and IIS.
Installing on Windows Server
Start PowerShell as admin and type the following to install IIS:
Install-WindowsFeature -name Web-Server -IncludeManagementTools
Installing on Windows 10
Start PowerShell as an admin and type the following to install IIS:
Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServer
The IIS role should be installed. The default site will point to the root folder of the C drive.
We can now proceed to download wsusscn2.cab from Microsoft.
Step 2. Download wsusscn2.cab
The link for this file can be tricky to find. You can either download it from this link and save it to the C drive or run the following script as admin on the IIS server:
# Default Site path, change if necessary
$IISFolderPath = "C:\inetpub\wwwroot\"
# Download wsusscn2.cab
Start-BitsTransfer -Source "http://go.microsoft.com/fwlink/?linkid=74689" -Destination "$IISFolderPath\wsusscn2.cab"
The script downloads the file to the wwwroot folder. We can verify the download by browsing to http:///wsusscn2.cab.
You also need to get the hash value of wsusscn2.cab to verify it. After saving it, run the following PowerShell command to check the file hash:
(Get-FileHash C:\inetpub\wwwroot\wsusscn2.cab).Hash
31997CD01B8790CA68A02F3A351F812A38639FA49FEC7346E28F7153A8ABBA05
Step 3. Run the check on a server
Next, you can use a PowerShell script to download and scan for missing updates on a PC or server using the wsusscn2.cab file. You can run the script on at least Windows Server 2008 or newer to avoid compatibility issues. To do this in a secure and effective manner over HTTP, we get the file hash of the downloaded wsusscn2.cab file and compare it with the file hash of the CAB file on the IIS server.
We can also use the file hash to see when Microsoft releases a new version of wsusscn2.cab.
Copy and save the following script as Get-MissingUpdates.ps1:
Param(
[parameter(mandatory)]
[string]$FileHash,
[parameter(mandatory)]
[string]$Wsusscn2Url
)
Function Get-Hash($Path){
$Stream = New-Object System.IO.FileStream($Path,[System.IO.FileMode]::Open)
$StringBuilder = New-Object System.Text.StringBuilder
$HashCreate = [System.Security.Cryptography.HashAlgorithm]::Create("SHA256").ComputeHash($Stream)
$HashCreate | Foreach {
$StringBuilder.Append($_.ToString("x2")) | Out-Null
}
$Stream.Close()
$StringBuilder.ToString()
}
$DataFolder = "$env:ProgramData\WSUS Offline Catalog"
$CabPath = "$DataFolder\wsusscn2.cab"
# Create download dir
mkdir $DataFolder -Force | Out-Null
# Check if cab exists
$CabExists = Test-Path $CabPath
# Compare hashes if download is needed
if($CabExists){
Write-Verbose "Comparing hashes of wsusscn2.cab"
$HashMatch = $Hash -ne (Get-Hash -Path $CabPath)
if($HashMatch){
Write-Warning "Filehash of $CabPath did not match $($FileHash) - downloading"
Remove-Item $CabPath -Force
}
Else{
Write-Verbose "Hashes matched"
}
}
# Download wsus2scn.cab if it dosen't exist or hashes mismatch
if(!$CabExists -or $HashMatch -eq $false){
Write-Verbose "Downloading wsusscn2.cab"
# Works on Windows Server 2008 as well
(New-Object System.Net.WebClient).DownloadFile($Wsusscn2Url, $CabPath)
if($Hash -ne (Get-Hash -Path $CabPath)){
Throw "$CabPath did not match $($FileHash)"
}
}
Write-Verbose "Checking digital signature of wsusscn2.cab"
$CertificateIssuer = "CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
$Signature = Get-AuthenticodeSignature -FilePath $CabPath
$SignatureOk = $Signature.SignerCertificate.Issuer -eq $CertificateIssuer -and $Signature.Status -eq "Valid"
If(!$SignatureOk){
Throw "Signature of wsusscn2.cab is invalid!"
}
Write-Verbose "Creating Windows Update session"
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateServiceManager = New-Object -ComObject Microsoft.Update.ServiceManager
$UpdateService = $UpdateServiceManager.AddScanPackageService("Offline Sync Service", $CabPath, 1)
Write-Verbose "Creating Windows Update Searcher"
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$UpdateSearcher.ServerSelection = 3
$UpdateSearcher.ServiceID = $UpdateService.ServiceID.ToString()
Write-Verbose "Searching for missing updates"
$SearchResult = $UpdateSearcher.Search("IsInstalled=0")
$Updates = $SearchResult.Updates
$UpdateSummary = [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
MissingUpdatesCount = $Updates.Count
Vulnerabilities = $Updates | Foreach {
$_.CveIDs
}
MissingUpdates = $Updates | Select Title, MsrcSeverity, @{Name="KBArticleIDs";Expression={$_.KBArticleIDs}}
}
Return $UpdateSummary
Run the script on one of the servers of computers to check for missing updates. To do this, copy the script to the machine and run the script with the URL to the wsusscn2.cab on the IIS server and the hash value from step two:
PS51> Get-MissingUpdates.ps1 -Wsusscn2Url "http:///wsusscn2.cab" -FileHash 31997CD01B8790CA68A02F3A351F812A38639FA49FEC7346E28F7153A8ABBA05
If there are missing updates, you should see output similar to the following:
ComputerName MissingUpdatesCount Vulnerabilities MissingUpdates
------------ ------------------- --------------- --------------
UNSECURESERVER 14 {CVE-2006-4685, CVE-2006-4686,
CVE-2019-1079, CVE-2019-1079...} {@{Title=MSXML 6.0 RTM Security Updat
If the machine is not missing updates, then you should see this type of output:
ComputerName MissingUpdatesCount Vulnerabilities MissingUpdates
------------ ------------------- --------------- --------------
SECURESERVER 0
The script gives a summary of the number of missing updates, what those updates are and the vulnerabilities they patch.
This process is a great deal faster than searching for missing updates online. But this manual method is not efficient when checking a fleet of servers, so let's learn how to run the script on all systems and collect the output.
Step 4. Run the scanning script on multiple servers at once
The easiest way to collect missing updates from all servers with PowerShell is with a PowerShell job. The PowerShell jobs run in parallel on all computers, and you can fetch the results.
On a PC or server, save the file from the previous step to the C drive -- or another directory of your choice -- and run the following as a user with admin permissions on your systems:
# The servers you want to collect missing updates from
$Computers = @(
'server1',
'server2',
'server3'
)
# These are the arguments that will be sent to the remote servers
$RemoteArgs = @(
# File hash from step 2
"31997CD01B8790CA68A02F3A351F812A38639FA49FEC7346E28F7153A8ABBA05",
"http://$env:COMPUTERNAME/wsusscn2.cab"
)
$Params = @{
ComputerName = $Computers
ArgumentList = $RemoteArgs
AsJob = $True
# Filepath to the script on the server/computer you are running this command on
FilePath = "C:\Scripts\Get-MissingUpdates.ps1"
# Maximum number of active jobs
ThrottleLimit = 20
}
$Job = Invoke-Command @Params
# Wait for all jobs to finish
$Job | Wait-Job
# Collect Results from the jobs
$Results = $Job | Receive-Job
# Show results
$Results
This runs the Get-MissingUpdates.ps1 script on all servers in the $Computers variable in parallel to save time and make it easier to collect the results.
You should run these PowerShell jobs regularly to catch servers with a malfunctioning Windows Update and to be sure important updates get installed.