Finding the Real "Last Patched" Day (Interim Version)

Published: 2022-05-03
Last Updated: 2022-05-04 01:57:14 UTC
by Rob VandenBrink (Version: 1)
0 comment(s)

I've been using a PowerShell script since forever that enumerates the patch dates across an AD domain.  Yesterday I found a use case where it was broken.

I found a number of servers that received an update last month for Windows Update itself (KB5011570: Servicing stack update for Windows 10, version 1607 and Server 2016: March 8, 2022).  However, they did not install The April or in some cases even the March cumulative updates.  In the graphic below, you'll see that "Servicing stack update" KB5011570, first in the list, with February's Cumulative update (KB5010359) as SECOND.  If I'm measuring patch compliance for a client's domain, or looking for servers that have missed a patch cycle, it's that second patch I'm interested in.

However, my previous script happily enumerated the "last patch date" and found that April update of course, which should not have counted (given what I'm enumerating for)

So, how to fix this?  The easy answer is "look for the word "Cumulative" in the patch description.  It was at this point that I discovered that the get-hotfix and the gwmi methods of collecting patches run against the local machine, and the actual plain-language text description of the update is NOT kept locally, you need to make a web request using the KB number to get that!  These commands collect the KB number and call it a day.  The web request you woudl make from a browser (more on this later) to collect the "real" description looks like:$kbnum

This collects a full HTML page with a TON of information:

You can parse this until you reach sub-atomic particles, but in my script all I'm interested in is if the word "Cumulative" exists in the patch titles.  We find this in the "outertext" section of the HTML that's returned - which you get from:
$returntable = $WebResponse.ParsedHtml.body.getElementsByTagName("table") | Where {$_.className -match "resultsBorder"}
write-host $returntable.outertext   # only needed for debugging and illustration

Now add a quick check:
if ($returntable.outertext -like "*Cumulative*")

Before I make these calls, I sort the full patch list in descending order, so I can start from the most recent one, looking for the newest patch with that key word "Cumulative".  The final script is in my github at:

And yes, I will be creating a CIS Controls Version 8 set of scripts (sometime soon).

The final script, with these changes is:

$pcs = get-adcomputer -filter * -property Name,OperatingSystem,Operatingsystemversion,LastLogonDate,IPV4Address
$patchinfo = @()
$count = $pcs.count
foreach ($pc in $pcs) {
    # keep total progress count
    write-host "Host" $i "of" $count "is being checked"
    if (Test-Connection -ComputerName $pc.DNSHostName -count 2 -Quiet) {
        # echo the host being assessed (only live hosts hit this print)
        write-host $pc.dnshostname "is up, and is being assessed"
        $tempval = new-object psobject
        $hfs = get-hotfix -computername $pc.dnshostname | sort -descending InstalledOn
        # look only for the latest Cumulative update
        foreach ($hf in $hfs) {
                $kbnum = $hf.hotfixid
                $WebResponse = Invoke-WebRequest "$kbnum"
                $returntable = $WebResponse.ParsedHtml.body.getElementsByTagName("table") | Where {$_.className -match "resultsBorder"}
                # write-host $returntable.outertext    # no need to write this to the screen unless debugging
                if ($returntable.outertext -like "*Cumulative*")  {
                     $lasthf = $hf
        $tempval | add-member -membertype noteproperty -name Name -value $pc.dnshostname
        $tempval | add-member -membertype noteproperty -name PatchDate -value $lasthf.installedon
        $tempval | add-member -membertype noteproperty -name OperatingSystem -value $pc.OperatingSystem
        $tempval | add-member -membertype noteproperty -name OperatingSystemVersion -value $pc.OperatingSystemVersion
        $tempval | add-member -membertype noteproperty -name IpAddress -value $pc.IPV4Address
        $tempval | add-member -membertype noteproperty -name LastLogonDate -value $pc.LastLogonDate
        $patchinfo += $tempval
$patchinfo | export-csv -path ./patchdate.csv


This script is by no means done!  This is a quick-and-dirty "how can I get the info really quickly" script, since I needed it before planning a series of updates.  There's definitely an API for this, and Microsoft has also published a graphql approach (which is WAY too complicated for what I'm collecting)

I also found a nice module in PowershellGallery "PSWindowsUpdate", but it doesn't seem to work 100% yet - the get-wuhistory command hangs fairly consistently.  (  Once that's fixed though it'll be a good way to go!.  

The WindowsUpdateProvider module from Microsoft also looks great, but is native to W11 and S2019 - something version agnostic that doesn't need to be installed would be ideal.  Lots of us run scripts like this on customer servers, so you can't depend on a specific OS version, and installing additional tools that the client hasn't approved is also generally frowned upon  ..

What I'm really looking for is a a good, clear PowerShell call using a supported API for this, which doesn't require an third party module and isn't OS or PowerShell version-specific.  My google-fu isn't finding this today, so for now I have the approach above - but I certainly have not given up.  If anyone can point me at such a thing, I'll gladly update the script above and repost it when it's finished.  The method I've described above works, but will only work until some dev at Microsoft decides to change that Update Catalog results page.

Enjoy! (and a follow up to come soon!)

Rob VandenBrink

0 comment(s)

Some Honeypot Updates

Published: 2022-05-03
Last Updated: 2022-05-03 12:05:36 UTC
by Johannes Ullrich (Version: 1)
0 comment(s)

Yesterday I made live some updates to our honeypot. The honeypot has gained popularity, and users have adapted it to different platforms. We continue to work on it, and significant updates will hopefully be ready soon, but for now, I released an update fixing some bugs and, most of all, updated some of the instructions. Thanks to our undergraduate interns for helping with this.

1 - Cowrie Update

We use cowrie to collect telnet and ssh logs (thanks, Michel Oosterhof, for maintaining this great tool). To avoid breaking any dependencies with our end of the log processing, we include a bundled version of cowrie vs. just pulling the most current one from Github. 

2 - Updated Raspberry Pi Installation

The preferred way to install our honeypot is a Raspberry Pi. The Raspberry Pi project has released an "Imager" tool to streamline the initial install experience. The "Imager" tool allows you to create a customized image with ssh enabled, a custom username and password, and upload ssh keys. You first had to create an image and later log in to the Pi to make these adjustments in the past.

3 - Improved Ubuntu Version

Raspberry Pis are in short supply right now. Another popular alternative to installing the honeypot is to use a virtual machine running Ubuntu. Earlier versions of the honeypot did not install the firewall rules correctly, leading to missed logs. This has been fixed now. Note that the honeypot install will not preserve any firewall rules you may have had before installing the honeypot.

4 - Azure Install

Again thanks to our interns, the honeypot now works better with Azure. We always sort of supported AWS (it is just a VM, after all), but now, you may use Azure.

The update should apply automatically if you have automatic updates enabled. If not, you will receive an email reminding you to update manually in a few days. Please report any bugs via GitHub.


Johannes B. Ullrich, Ph.D. , Dean of Research,

0 comment(s)
ISC Stormcast For Tuesday, May 3rd, 2022


What's this all about ..?
password reveal .
<a hreaf="">the social network</a> is described as follows because they respect your privacy and keep your data secure:

<a hreaf="">the social network</a> is described as follows because they respect your privacy and keep your data secure. The social networks are not interested in collecting data about you. They don't care about what you're doing, or what you like. They don't want to know who you talk to, or where you go.

<a hreaf="">the social network</a> is not interested in collecting data about you. They don't care about what you're doing, or what you like. They don't want to know who you talk to, or where you go. The social networks only collect the minimum amount of information required for the service that they provide. Your personal information is kept private, and is never shared with other companies without your permission
<a hreaf=""> public bathroom near me</a>
<a hreaf=""> nearest public toilet to me</a>
<a hreaf=""> public bathroom near me</a>
<a hreaf=""> public bathroom near me</a>
<a hreaf=""> nearest public toilet to me</a>
<a hreaf=""> public bathroom near me</a>
Enter comment here... a fake TeamViewer page, and that page led to a different type of malware. This week's infection involved a downloaded JavaScript (.js) file that led to Microsoft Installer packages (.msi files) containing other script that used free or open source programs.
distribute malware. Even if the URL listed on the ad shows a legitimate website, subsequent ad traffic can easily lead to a fake page. Different types of malware are distributed in this manner. I've seen IcedID (Bokbot), Gozi/ISFB, and various information stealers distributed through fake software websites that were provided through Google ad traffic. I submitted malicious files from this example to VirusTotal and found a low rate of detection, with some files not showing as malware at all. Additionally, domains associated with this infection frequently change. That might make it hard to detect.
Enter corthrthmment here...

Diary Archives