Get the status of WSUS clients installing the September Cumulative Update

A few days ago, a list member asked the following question:

I’m trying to find out what the status is on clients installing the September Cumulative Update.

He also reported that he was using WID (Windows Internal Database) and not SQL. He was also struggling with the Microsoft Report Viewer and Microsoft System CLR Types for SQL Server.

I replied that he can achieve this using only #PowerShell and the WSUS API 😀 but my first try was:

$updateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$updateScope.TextIncludes= '4038777'
(Get-WsusServer).GetUpdateApprovals($updateScope) | 
ForEach-Object {
 $tg = (Get-WsusServer).GetComputerTargetGroup($($_.ComputerTargetGroupId))
 Write-Verbose -Message "Dealing with approval type $($_.Action) on to target group '$($tg.Name)'" -Verbose
 $tg.GetComputerTargets($true) | 
 ForEach-Object {
  $computer = $_
   $State = $computer.GetUpdateInstallationSummary($updateScope)
    ComputerName = $_.FullDomainName ;
    Unknown = $( if($State.UnknownCount) { $true } else { $false} );
    NotApplicable = $( if($State.NotApplicableCount) { $true } else { $false } );
    NotInstalled = $( if($State.NotInstalledCount) { $true } else { $false } );
    Downloaded = $( if($State.DownloadedCount) { $true } else { $false } );
    Installed = $( if($State.InstalledCount) { $true } else { $false } );
    InstalledPendingReboot = $( if($State.InstalledPendingRebootCount) { $true } else { $false } );
    Failed = $( if($State.FailedCount) { $true } else { $false } );
} | ogv

Well, the above code did the job but performed very poorly. It took more than a minute to display the results for a hundred client computers.

I took the update scope approach with a custom text filter and I’ve been inspired by my previous blog post about WSUS reporting.

I wasn’t satisfied and I believed I took the wrong approach and that there should be one or more efficient ways to get the results.
So, the next morning, I gave it another try and found another way to skin the cat:

$kb = '4038777' | % { (Get-WsusServer).SearchUpdates($_) } | ? Title -match 'Windows 7 for x64-based ' |  ? { $_.IsLatestRevision }
(Get-WsusServer).GetComputerTargetGroups() | 
ForEach-Object {
 $kb.GetUpdateInstallationInfoPerComputerTarget($_)  | 
 ForEach-Object {
   Computer = (Get-WsusServer).GetComputerTarget($_.ComputerTargetId).FullDomainName
   State = $_.UpdateInstallationState
} | ogv

The above way performs much faster (max 8 seconds for the same 100 computers) and has less code 😎


PSGallery and catalog files

I’ve been using catalog files associated with modules published on the PowerShellGallery the wrong way and got a warning from the PowerShell gallery administrator because my catalog files were breaking the Install-Module cmdlet experience.

As of PowerShell 5.1, you can create catalog files with the new New-FileCatalog cmdlet. Let’s quickly see what is a catalog file?

A digitally-signed catalog file (.cat) can be used as a digital signature for an arbitrary collection of files. A catalog file contains a collection of cryptographic hashes, or thumbprints. Each thumbprint corresponds to a file that is included in the collection.

(Source) May I insist on “can be used” in the above definition 🙄

That’s what I initially did. I was using catalog files as containers for a collection of file hashes. I didn’t use the catalog file as a digital signature and didn’t sign digitally catalog files. Catalog files allowed me to replace CSV files that I stored in my ADK repository and have a much better solution to check the integrity of each ADK files downloaded using the Test-FileCatalog cmdlet. See the following commit. The core functionality of catalog files just served very well my scenario where I just wanted to check that files downloaded match their hash.
Just a quick digression about performance. In the above scenario, using Get-AuthenticodeSignature (yes, all ADK files are digitally signed 😀 ) is the slowest (took 1 minute 28 seconds) 😦 , using Test-FileCatalog took 46 seconds 🙂 and my CSV file with the Get-FileHash approach took only 28 seconds 😎 .

The code signing requirements for modules on the PowerShell gallery however are much higher than what I did with the ADK files. These requirements can be found in the PowerShellGallery Publishing Guidelines and Best Practices
modulo the fact that Save-Module doesn’t validate the catalog file (issue introduced on GH)

I’ve acquired a code signing certificate and here are the steps I use to sign a module before publishing it to the gallery.

  • Commit last changes to the module in the master branch
  • git.exe push -u origin master

    Copy the local github repo for the module to ~/Documents\WindowsPowerShell\Modules\$MyModule without folders like .git

    robocopy $GHRepoSource ~/Documents\WindowsPowerShell\Modules\$MyModule /R:0 /Z /S /XD .git /XD .vscode /XF *.cat /NP
  • Use the latest version of the PSScriptAnalyzer to check if my module is compliant with coding best practices
  • # Run PSScriptAnalyzer
    Invoke-ScriptAnalyzer -Path ~/Documents\WindowsPowerShell\Modules\$MyModule -Recurse -Verbose
  • Sign the module and its manifest
  • # Sign the module
    $cert = Get-ChildItem Cert:\CurrentUser\My\ -CodeSigningCert | 
    Where { $_.HasPrivateKey -and ( $_.NotAfter -gt (Get-Date)) }
    Get-ChildItem ~/Documents\WindowsPowerShell\Modules\$MyModule -Include *.psd1,*.psm1 -Recurse |
    Set-AuthenticodeSignature -Certificate $cert -TimestampServer -Verbose -HashAlgorithm SHA256
    # at this stage only .psd1 and psm1 are signed
    Get-AuthenticodeSignature ~/Documents\WindowsPowerShell\Modules\$MyModule\*
  • Create the catalog file
  • # Create the catalog file
    New-FileCatalog -Path  ~/Documents\WindowsPowerShell\Modules\$MyModule -CatalogFilePath ~/Documents\WindowsPowerShell\Modules\$MyModule\ -CatalogVersion 2.0 -Verbose
  • Sign the catalog file
  • # Sign the catalog file
    Get-ChildItem ~/Documents\WindowsPowerShell\Modules\$MyModule\ -EA 0 |
    Set-AuthenticodeSignature -Certificate $cert -TimestampServer -Verbose -HashAlgorithm SHA256
  • Test the catalog file
  • # Test the catalog file
    Test-FileCatalog -Path ~/Documents\WindowsPowerShell\Modules\$MyModule -CatalogFilePath ~/Documents\WindowsPowerShell\Modules\$MyModule\ -Detailed 
    Get-AuthenticodeSignature ~/Documents\WindowsPowerShell\Modules\$MyModule\*

At this point everything is set and I can use the Publish-Module cmdlet.

Conclusion: If you want to use catalog files on PowerShellGallery, remember that you’ve to sign them digitally along with your regular module files.

PS5.1 import-pssession and $PSSenderInfo

Last month when I got back from holidays, helpdesk staff members complained that they could not reset user passwords anymore using the PowerShell constrained endpoint I setup almost a year ago.
They got a message saying Running the Get-Command command in a remote session reported the following error: A parameter cannot be found that matches parameter name ‘PowerShellVersion’

You know, there’s no tab completion in an interactive constrained endpoint. Your only friend is Get-Command.
So, to ease their pain with constrained endpoints, I usually create another script that imports the remote constrained commands using the Import-PSSession cmdlet.

What happened or changed during my summer holidays?
Before leaving, I started to deploy WMF 5.1 to computers mainly because:

As of June 1, 2017, users with WMF 5.0 must upgrade to WMF 5.1 to receive support.


Helpdesk members experienced this behavior after they rebooted their computers (WMF 5.1 installation wasn’t pending anymore). Entering interactively the constrained endpoint just works. It was only the import of the session that failed.

When I googled the error, there was already a github issue opened and a merge into the PowerShell Core branch. Nice, isn’t it? 😀

It appears that the server hosting the constrained endpoint was already running PowerShell 5.1 and connecting helpdesk workstations just shifted to this version and fall into this Import-PSSession issue.

What should I do?
As Joey Aiello said in this post:

Windows PowerShell 5.1, much like .NET Framework 4.x, will continue to be a built-in, supported component of Windows 10 and Windows Server 2016. However, it will likely not receive major feature updates or lower-priority bug fixes. With PowerShell Core, we are actively addressing bugs that may have existed in previous versions of Windows PowerShell.

The issue was fixed in a few days in PowerShell 6.0 (see and won’t be fixed in PowerShell 5.1 at the same speed unfortunately. Likely means “not” most of the time in this context unless it’s security related or really, really bad. The only issue fixed in PowerShell 5.0 as far as I know is cve-2017-8565.

Here’s the dilemma. Should I remove the only supported version of PowerShell from the server or from the clients?
Well, I chose the server because there are still some products that are incompatible with WMF 5.1 and 5.0.
I was lazy and didn’t even have to remove any PowerShell from any server.
I just found a Windows 2012 R2 running a default version of PowerShell, which is version 4.0.

I moved my module to the server and registered the constrained endpoint.
Now, I got another problem. I couldn’t get an alert telling me who did a password reset.
In PowerShell 5.1, I successfully used


To be able to get back the visibility on who did what, I had to use the following in PowerShell 4.0


I wonder why. Isn’t a restricted endpoint running in NoLanguage mode the same in PowerShell 4.0 and 5.x?
What’s the difference between the two syntaxes if $PSSenderInfo is just a read-only variable?

Using Get-Member, it looks the same, except that one is property and ConnectedUser appears to be a script property.

May I conclude and say that script properties on read-only variables are allowed in a restricted remote sessions running PowerShell 5.x in no lanugage mode?

NetCease module

Almost a year ago, Itai Grady released a script on the technet gallery that blocks the reconnaissance done by using the Server Message Block (SMB) Session Enumeration method.

I’ve studied, changed the script and made a module of it. Why?
It’s easier to share and install once it’s uploaded on the PowerShellGallery.
The approach inside the module is a bit different and allows to move more easily from the default state to the hardened state and vice-versa.
Let’s say that the module assumes breach and is somehow “more” idem-potent than the original version 😀

Let’s see quickly how to use it:

# Download the module
Save-Module -Name NetCease -Repository PSGallery -Path ~/Downloads
# Load the module
Import-Module ~/Downloads/NetCease/1.0.1/NetCease.psd1 -Force -Verbose
# View current NetSessionEnum permissions
Get-NetSessionEnumPermission | 
Select TranslatedSID,SecurityIdentifier,AccessMask,AceType | ft -AutoSize
# Harden permissions
Set-NetSessionEnumPermission -Verbose -Confirm:$false
# Restart the Server service for changes to take effect
Restart-Service LanmanServer -Force -Verbose

How to test if it works?
To quickly test if, I borrowed the pieces of code: the Get-NetSession function from @harmj0y and the PSReflect module from @mattifestation

In the above screenshot, you can see that the Get-NetSession doesn’t return anything after I hardened the configuration of the targeted Domain Controller.

Note that the Server service has some dependencies on other services. Restarting these services could cause a little disruption, so be careful.

The ATA gateway detected my previous attempts:

The module will work on Windows 7, 8.1, 10, Windows Server 2008 R2, 2012 R2 and 2016 and isn’t only for domain controllers. If you want to limit the recon performed by an insider attacker, you’ll want to apply it to every machine.

Bonus: I didn’t immediately realized it but it’s possible to create a Desired State Configuration (DSC) config 😎

Here’s the result of applying twice the DSC config and having first restored the default permissions:

Create Adobe GPO templates

Every good IT pro tries to follow best practices by updating and configuring workstations, software installed,…

Let’s consider specifically the PDF reader software provided by Adobe and the fact that the IT pro wants to harden the configuration to have a more resistant endpoint to this attack vector.

Adobe provides some GPO templates that you can find on their FTP website:

Version Reader Acrobat

If you load all these ADMX and ADML files in your policy definitions folder, you’ve got the following in the group policy editor console:

When you start to explore these templates and look for hardening the security settings, you get really disappointed because there’s only between 5 to 7 settings per node 😦

It’s a shame because Adobe took the time to document many registry settings in the Enterprise toolkit pages and for example on these pages:

But they failed to make these settings available in GPO templates 😦

Adobe fails, no problem, PowerShell to the rescue 😎

It appears I’m not the only one who thinks this way. The Information Assurance mission at NSA (iadgov) helps the Department of Defense (DoD) to apply baselines.
They have a huge github repository and even have an Adobe Reader DC template with around 45 settings. It’s not their first attempt. Before that they published recommended Adobe Reader XI settings.

However their single Adobe template has many problems that I won’t detail here when I loaded it on a Windows 7 workstation.
Kudos to iadgov! I’ve used some of their settings when appropriate, their categories but the main difference is that I created a PowerShell module that creates templates on demand for the Reader, Acrobat and their 2005, 2007 or DC versions 😎

I’d like the community to contribute to get more settings,… I’ve uploaded the module on github so that it’s easy to fork, track issues, follow changes.
I’ve also added a documentation of every settings on this page:

I’ve also uploaded the module on the PowerShell Gallery:

Here’s an overview of what you’ll get if you generate all the templates and move them to your local GPO templates folder:

That looks better, isn’t it? And there are more than 40 settings for each version of Adobe Software. 😀

  • How to start and create these templates?
    • Download the module
    • Find-Module -Name AdobeGPOTemplates -Repository PSGallery
      Save-Module -Name AdobeGPOTemplates -Path ~/Downloads -Repository PSGallery
      $HT = @{
       CatalogFilePath = "~/Downloads/AdobeGPOTemplates/1.0.0/"
       Path = "~/Downloads/AdobeGPOTemplates/1.0.0"
       Detailed = $true
       FilesToSkip = 'PSGetModuleInfo.xml'
      Test-FileCatalog @HT

    • Import the module, create templates and copy them to your local GPO templates folder
    • Import-Module ~/Downloads/AdobeGPOTemplates/1.0.0/AdobeGPOTemplates.psd1 -Force
      Get-Command New-AdobeGPOTemplate -Syntax
      # Get-Help New-AdobeGPOTemplate  -Examples
      New-AdobeGPOTemplate -Product Reader,Acrobat -Version DC,2017,2015
      copy .\*.admx -Destination C:\Windows\PolicyDefinitions\
      copy .\*.adml -Destination C:\Windows\PolicyDefinitions\en-US\

  • What’s the bare minimum config?
  • If you omit the first rule of hygiene that states that you need to update your software and the fact that the Adobe Reader has many “cloud-focused” features, I’d say that the 3 minimum settings to configure are:

    1. Disable JavaScript
    2. Disable the ability to execute any embedded object
    3. Have the protected view turned on for anything

    I know we may not agree and if you’ve an opinion about the bare minimum config, please share it in the comments.

    • Example of minimum config

    Let’s say you just want to change the following default settings at the user level without locking down everything based on the above 3 recommendations:

    After you configured the following GPO settings:

    You get this in the Reader UI:


If you’ve seen the following 3 words in recent updates like KB4025342, don’t panic,…

…keep calm and read the CVE-2017-8565 on the new security updates guide portal

Yes, CVE-2017-8565 was published on July 11, 2017.

Oleksandr Mirosh and Alvaro Muñoz from Hewlett-Packard Enterprise Security reported this vulnerability as far as we can see on the acknowledgments page.

On the following page, you can read a good description of the issue:

A remote code execution vulnerability exists in PowerShell when PSObject wraps a CIM Instance. An attacker who successfully exploited this vulnerability could execute malicious code on a vulnerable system.

In an attack scenario, an attacker could execute malicious code in a PowerShell remote session.

The update addresses the vulnerability by correcting how PowerShell deserializes user supplied scripts.

The above page states it’s a Remote Code Execution (RCE) vulnerability and I guess it’s because Remoting is involved in the attack scenario.

Here’s why I don’t 100% agree with this classification:
First, let’s quickly examine who can access remoting on a Windows 10 1703 workstation with the Get-PSSessionConfiguration cmdlet

  • NT AUTHORITY\INTERACTIVE (S-1-5-4) are Users who log on for interactive operation. This is a group identifier added to the token of a process when it was logged on interactively. The corresponding logon type is LOGON32_LOGON_INTERACTIVE. (source)
  • BUILTIN\Administrators (S-1-5-32-544)
    A built-in group. After the initial installation of the operating system, the only member of the group is the Administrator account. When a computer joins a domain, the Domain Admins group is added to the Administrators group. When a server becomes a domain controller, the Enterprise Admins group also is added to the Administrators group (source)
  • BUILTIN\Remote Management Users (S-1-5-32-580) A Builtin Local group. Members of this group can access WMI resources over management protocols (such as WS-Management via the Windows Remote Management service). This applies only to WMI namespaces that grant access to the user. (source)

Based on the 3 groups listed above who can connect via remoting, either you already are an admnistrator of the box or are logged interactively (you’re an admin or a standard user) and/or belong to the local BUILTIN\Remote Management Users group.

If you’re not already an administrator, you could eventually become one by exploiting the CVE-2017-8565 vulnerability through remoting sessions.
In this case, I’m more likely to call this kind of a vulnerability an Elevation of Privilege (EoP) than a Remote Code Execution (RCE).


Invoking code via PowerShell Remoting is the prime purpose of PowerShell Remoting


Now let’s say, other remoting configurations were added because you’ve implemented JEA (Just Enough Administration) or constrained remoting endpoints with a RunAs account (a privileged account) to delegate access to some (less privileged) remote users (typically helpdesk members).
Again a less privileged remote user who was given access to a (vulnerable) remoting configuration/session could gain more privileges by exploiting this vulnerability.

We can see more details about this vulnerability in PowerShell Core. The beauty of open source projects is that they tend to be more transparent and agile in fixing bugs.

The issue with CIM deserializer was introduced in the PowerShell Core github repository the day after the security updates were released for regular Windows versions of PowerShell.
And it was fixed in about 3 days.
You can see the related merged Pull Request using this link.

I’ve tested this vulnerability on Windows 10 1703 fully patched where KB4025342 was installed. Importing the corrupted CIM class didn’t spawn a calculator process.

I’ve removed KB4025342 and restarted the computer

wusa.exe /uninstall /kb:4025342 /norestart

Importing the corrupted CIM class launched a calculator process. Its integrity was labeled “AppContainer” and has actually a lower integrity level than its parent process (wsmprovhost.exe) level set to medium (as I run as a standard user).

Importing the corrupted CIM class has the same result as doing the following already allowed:

Invoke-Command -ComputerName . -EnableNetworkAccess { calc.exe }

No elevation of privilege so far.

Now, let’s say I add a somehow quite restricted endpoint (allowing only import-clixml and access to the filessystem provider)

$HT = @{
 SchemaVersion = '1.0'
 ExecutionPolicy = 'Restricted'
 SessionType = 'RestrictedRemoteServer'
 LanguageMode = 'NoLanguage'
 VisibleCmdlets = 'Import-Clixml'
 VisibleProviders = 'FileSystem'
 RunAsVirtualAccount = $true
New-PSSessionConfigurationFile -Path C:\config.pssc @HT
Register-PSSessionConfiguration -Path 'C:\config.pssc' -Name "Test" -Force

Importing the corrupted CIM class results in the ability to escape the constrained endpoint restrictions – the “sandbox” – and run arbitrary code under a more privileged account. You can see that wsmprovhost.exe runs under a WinRM virtual user, has a High integrity level and has two child processes that inherited from its security context (cmd.exe and openwith.exe triggred by trying to run calc):

In my opinion, there’s no need to panic.
Arbitrary code can only run in a higher security context only in some specific scenarios.
This vulnerability can be easily remediated by just applying already available Windows security updates.

On a Windows 7, only Administrators can connect through remoting by default: