Easily identify your WSUS updates

I’ve been using some piece of code on WSUS to enrich non-security updates to help me identify what problem they address.

Let’s first have a look at the result as a picture is worth a thousand words 😀

Now, let’s see the code

(Get-WsusServer).SearchUpdates('Windows 7')| Where {
(-not($_.IsSuperseded)) -and
($_.Title -match "x64") -and
# ($_.UpdateClassificationTitle -eq "Security Updates") -and
(-not($_.isApproved)) -and
($_.IsLatestRevision) -and
(-not($_.isDeclined)) -and
($_.Title -notmatch '.NET Framework 4') -and
($_.Title -notmatch 'Internet Explorer (9|10)')
} | ForEach-Object {
$update = $_
$TitleLine = $pageTitle = $URL = $null
try {
$URL = ($_ | Select -expand AdditionalInformationUrls) -replace 'http://support\.microsoft\.com/kb',
Write-Verbose -Message "Downloading from $($URL)" -Verbose
$TitleLine = (new-object Net.Webclient).DownloadString([system.Uri]$($URL)) -split '\<' |
Select-string -Pattern 'h1.*title\=' -ErrorAction Stop | Select -Expand Line
if ($TitleLine) {
$pageTitle = ([regex]'h1\sid\=".*"\s*title\="(?<Title>.*)"\s*class=').Matches($TitleLine) |
Select -Last 1 -expand Groups | Select -Last 1 -Expand Value
$_ | Add-Member -MemberType NoteProperty -Name "Page Title" -Value $pageTitle -Force -PassThru -ErrorAction Stop
} else {
Write-Warning -Message "Failed to find title for $($update.Title)"
$update | Add-Member -MemberType NoteProperty -Name "Page Title" -Value $($update.Description) -Force -PassThru
} catch {
Write-Warning -Message "Failed for $($update.Title) because $($_.Exception.Message)"
$update | Add-Member -MemberType NoteProperty -Name "Page Title" -Value $($update.Description) -Force -PassThru
} | Select Title,State,'Page Title' | Out-GridView

I wanted to share this a long time ago but the initial piece of code broke while Microsoft was upgrading their support.microsoft.com website.
It may broke again of course…

Switch WSUS to https

The following was presented at Black Hat 2015 USA during the summer.

The whitepaper is available at http://ctx.is/WSUSpect
The slides are available at http://www.contextis.com/documents/162/WSUSpect_Presentation.pdf
The code is available at https://github.com/ctxis/wsuspect-proxy

The only mitigation identified was an HTTPS-enabled WSUS. In this post, I’d like to show how to switch your HTTP based WSUS to HTTPS.

I’ll assume that:

  • your server is running Windows 2012 R2, has already an http based WSUS
  • your server may not be connected directly to the Internet
  • you don’t want to buy a certificate from a public certiticate authority
  • you don’t have PKI in your infrastructure

This official documentation is available on Technet on this page

But let’s do this with PowerShell 🙂

# 1. Create a self-signed certificate
$SelfSignedHT = @{
 DnsName = "$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower()
 CertStoreLocation = "Cert:\LocalMachine\My"
New-SelfSignedCertificate @SelfSignedHT
$cert = Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication

# 2. Export its public key
Export-Certificate -Cert $cert -Type CERT -FilePath ~/documents/cert.cer

# 3. Import the public key in the Trusted Root Certificate Authorities store
Import-Certificate -FilePath ~/documents/cert.cer -CertStoreLocation Cert:\LocalMachine\Root

# 4. Select this certificate in the SSL bindings
$cert | New-Item IIS:\SslBindings\!8531

# 5. Require SSL for the following virtual roots only:
'ClientWebService' | ForEach-Object {
 Set-WebConfigurationProperty -Filter 'system.webserver/security/access' -Location "WSUS Administration/$($_)" -Name sslFlags -Value 8

# 6. Switch WSUS to SSL
& 'C:\Program Files\Update Services\Tools\WsusUtil.exe' configuressl $("$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower())

# 7. Change your GPO to point to the new URL
$key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$uri = 'https://{0}:8531' -f $("$($env:COMPUTERNAME).$($env:USERDNSDOMAIN)".ToLower())
Get-GPO -All | Foreach-Object { 
 if ($_ | Get-GPRegistryValue -Key $key -ValueName WUServer -EA 0) {
  $_ | Set-GPRegistryValue -Key $key -ValueName WUServer       -Value $uri -Type String
  $_ | Set-GPRegistryValue -Key $key -ValueName WUStatusServer -Value $uri -Type String

You’re almost done.
The last step consists in distributing the public key of your self-signed certificate to your clients so that they trust your self-signed certificate.
To achieve this, you’ll need to edit your GPO that targets clients and import your ~/documents/cert.cer file into ‘Computer Configuration / Policies / Windows Settings / Security Settings / Public Key Policies / Trusted Root Certificate Authrorities

You’ve successfully mitigated the threat reported at BH2015 😎
Note that the certificate is valid for 1 year.