Retours sur le 1er #FRPSUG

Le 1er RDV gratuit et 100% virtuel du French PowerShell User Group (#FRPSUG) a bien eu lieu et a été un franc succès 🙂

Si vous l’avez râté, vous pouvez revoir l’enregistrement sur cette page https://frpsug.github.io/2016/09/20/FrPSUG01-Enregistrement, vous y trouverez également la présentation et le code présentés.

Comment rejoindre la communauté?

Get the TLS ciphers suite order

Last year, Microsoft published an advisory about a vulnerability in Schannel where weak/insecure ciphers were used in TLS sessions. More recently Microsoft also published an Update to add new cipher suites to Internet Explorer and Microsoft Edge in Windows.

In the above advisory, they introduced a GPO setting where you can set a new ciphers suite order. Nice, I love it.
But wait, without that GPO setting,…

  • How do I know what is the order of ciphers being used?

The question was answered on this stackoverflow.com forum page
Unfortunately it doesn’t work with PowerShell 2.0 (default version) on Windows 7 and I get the following error
inptrsize-operator

  • What about newer systems?

The code proposed on the stackoverflow.com forum page works in Windows 8.1 and PowerShell 4.0. There’s also a module called TLS but it doesn’t have the Get-TlsCipherSuite cmdlet 😦
tls-module-on-windows81

On Window 10, you’ve got more in the TLS module and the Get- TlsCipherSuite is available 🙂

On Windows 10 to get the order of ciphers, you simply do

(Get-TlsCipherSuite).Name
  • How can I get the order of ciphers whatever the operating system and its version of PowerShell?

I’ve slightly changed the code proposed on the stackoverflow.com forum page : line 49 replaced by 48 😎

Try {
# http://stackoverflow.com/questions/19695623/how-to-call-schannel-functions-from-net-c
if (-not ([System.Management.Automation.PSTypeName]'ConsoleApplication4').Type) {
Add-Type -TypeDefinition @'
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication4
{
public class Program
{
[DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
static extern uint BCryptEnumContextFunctions(uint dwTable, string pszContext, uint dwInterface, ref uint pcbBuffer, ref IntPtr ppBuffer);
[DllImport("Bcrypt.dll")]
static extern void BCryptFreeBuffer(IntPtr pvBuffer);
[DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
static extern uint BCryptAddContextFunction(uint dwTable, string pszContext, uint dwInterface, string pszFunction, uint dwPosition);
[DllImport("Bcrypt.dll", CharSet = CharSet.Unicode)]
static extern uint BCryptRemoveContextFunction(uint dwTable, string pszContext, uint dwInterface, string pszFunction);
[StructLayout(LayoutKind.Sequential)]
public struct CRYPT_CONTEXT_FUNCTIONS
{
public uint cFunctions;
public IntPtr rgpszFunctions;
}
public const uint CRYPT_LOCAL = 0x00000001;
public const uint NCRYPT_SCHANNEL_INTERFACE = 0x00010002;
public const uint CRYPT_PRIORITY_TOP = 0x00000000;
public const uint CRYPT_PRIORITY_BOTTOM = 0xFFFFFFFF;
public static void DoEnumCiphers()
{
uint cbBuffer = 0;
IntPtr ppBuffer = IntPtr.Zero;
uint Status = BCryptEnumContextFunctions(
CRYPT_LOCAL,
"SSL",
NCRYPT_SCHANNEL_INTERFACE,
ref cbBuffer,
ref ppBuffer);
if (Status == 0)
{
CRYPT_CONTEXT_FUNCTIONS functions = (CRYPT_CONTEXT_FUNCTIONS)Marshal.PtrToStructure(ppBuffer, typeof(CRYPT_CONTEXT_FUNCTIONS));
IntPtr pStr = functions.rgpszFunctions;
for (int i = 0; i < functions.cFunctions; i++)
{
Console.WriteLine(Marshal.PtrToStringUni(Marshal.ReadIntPtr(pStr)));
pStr = new System.IntPtr((pStr.ToInt64()+(IntPtr.Size))) ;
// pStr += IntPtr.Size;
}
BCryptFreeBuffer(ppBuffer);
}
}
}
}
'@ -ErrorAction Stop
} else {
Write-Verbose -Message "Type already loaded" -Verbose
}
# } Catch TYPE_ALREADY_EXISTS
} Catch {
Write-Warning -Message "Failed because $($_.Exception.Message)"
}
[ConsoleApplication4.Program]::DoEnumCiphers()

Additional links
Documented ciphers suites per OS
Update to enable TLS 1.1 and TLS 1.2 as a default secure protocols in WinHTTP in Windows
BCryptEnumContextFunctions function
CRYPT_CONTEXT_FUNCTIONS structure
BCryptFreeBuffer function

Testing WSUS server operational status

During the summer, someone asked the following questionwsus-monitoring-question on the WSUS patchmanagement.org mailing list.

I replied and immediately thought that Pester would do the job and quickly showed how he could test if he can connect to the console.

I’ve actually more than a WSUS server to manage. So, I started separating the environmental configuration data from the pester tests code almost the same way Mike F. Robbins did in his recent post where he goes far beyond to what I did.

I think it’s a great idea and here’s what I did in my case to monitor my WSUS server operational status.

To get started, I copied the Pester module on my WSUS server, imported the module and did in the ISE:

# helper to create the required files and folder if not present
New-Fixture -Path  ~/Documents/Pester -Name Test-WSUS
# Put the config data into that file:
psEdit ~/Documents/Pester/Test-WSUS.ps1
# Put the pester code into that file:
psEdit ~/Documents/Pester/Test-WSUS.Tests.ps1

After the first 3 commands, here’s what the ISE console looked liked
pester-new-fixture-wsus-tests

The first file Test-WSUS.ps1 looks like this by default.
It will be used to store my configuration data.
pester-wsus-tests-01

The second file Test-WSUS.Tests.ps1 is where I’ll write the pester tests code
pester-wsus-tests-02

After editing the 1rst file Test-WSUS.ps1 like this:
pester-test-wsus-file (fake data in this case)

…and the 2nd file Test-WSUS.Tests.ps1 like this:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"
Describe 'Testing WSUS server locally' {
Context 'Windows Features' {
BeforeAll {
$WindowsFeatures = Get-WindowsFeature
}
# 'Web-Server',
#'Web-WebServer',
# 'Web-Common-Http',
'Web-Default-Doc',
'Web-Static-Content',
# 'Web-Performance',
'Web-Dyn-Compression',
# 'Web-Security',
'Web-Filtering',
'Web-Windows-Auth',
#'Web-App-Dev',
'Web-Net-Ext45',
'Web-Asp-Net45',
'Web-ISAPI-Ext',
'Web-ISAPI-Filter',
# 'Web-Mgmt-Tools',
'Web-Mgmt-Console',
# 'Web-Mgmt-Compat',
'Web-Metabase',
# 'UpdateServices',
'UpdateServices-WidDB',
'UpdateServices-Services',
# 'NET-Framework-45-Features',
'NET-Framework-45-Core',
'NET-Framework-45-ASPNET',
# 'NET-WCF-Services45',
'NET-WCF-TCP-PortSharing45',
#'RSAT-Role-Tools',
#'UpdateServices-RSAT',
'UpdateServices-API',
'UpdateServices-UI',
#'User-Interfaces-Infra',
'Server-Gui-Mgmt-Infra',
'Windows-Internal-Database',
#'PowerShellRoot',
'PowerShell',
#'PowerShell-ISE',
#'WAS',
'WAS-Config-APIs',
'WoW64-Support' | ForEach-Object {
$name = $_
It "Feature $($_) should be Installed" {
($WindowsFeatures | Where Name -eq $name).Installed | Should be $true
}
}
}
Context 'IIS' {
BeforeAll {
Import-Module Webadministration -ErrorAction SilentlyContinue
}
It 'Certiticate should be imported in the Cert:\localmachine\My' {
Test-Path -Path "Cert:\LocalMachine\my\$($ThumbPrint)" | Should Be $true
}
It 'Certificate should be valid' {
(dir -Path "Cert:\LocalMachine\my\$($ThumbPrint)").NotAfter.toString('dd/MM/yyyy HH:mm') | Should Be $CertExpiryDate
}
It 'Certiticate should be present in IIS' {
(dir -Path IIS:\SslBindings\0.0.0.0!$($Port)).Thumbprint | should be "$($ThumbPrint)"
}
}
Context 'Services' {
It 'the WsusService service is in Automatic start mode' {
(Get-WmiObject -Query "Select * FROM Win32_service WHERE Name = 'WsusService'").StartMode | Should Be 'Auto'
}
It 'the WsusService service is running' {
(Get-Service -Name WsusService).Status | Should Be 'Running'
}
}
Context 'Firewall' {
Get-NetFirewallProfile | ForEach-Object {
It "Firewall profile $($_.Name) is Enabled" {
$_.Enabled | Should be $true
}
}
Get-NetFirewallRule -Name 'IIS-WebServerRole-HTTPS-In-TCP' | ForEach-Object {
It "Firewall rule $($_.Name) should be Enabled" {
$_.Enabled | Should be $true
}
It "Firewall rule $($_.Name) action should be Allow" {
$_.Action | Should be 'Allow'
}
}
Get-NetFirewallRule -Name 'IIS-WebServerRole-HTTP-In-TCP' | ForEach-Object {
It "Firewall rule $($_.Name) should be Enabled" {
$_.Enabled | Should be $true
}
It "Firewall rule $($_.Name) action should be Allow" {
$_.Action | Should be 'Allow'
}
}
}
Context 'Console connection' {
BeforeAll {
Add-Type -Path "$($env:ProgramFiles)\Update Services\Api\Microsoft.UpdateServices.Administration.dll"
}
It 'should connect to the console' {
{ [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer("$($FQDN)",$true,$Port) } | Should Not Throw
}
}
Context 'Configuration' {
BeforeAll {
$WSUSServer = Get-WsusServer
$DB = (Get-WsusServer).GetDatabaseConfiguration()
$WSUSConfig = (Get-WsusServer).GetConfiguration()
}
It 'Shoule be ready' {
((Get-WsusServer).GetConfiguration().GetUpdateServerConfigurationState()).value__ | Should be '0'
}
It 'BypassApiRemoting should be False' {
$WSUSServer.BypassApiRemoting | Should be $false
}
It 'IsConnectionSecureForApiRemoting should be True' {
$WSUSServer.IsConnectionSecureForApiRemoting | Should be $true
}
It 'IsServerLocal should be True' {
$WSUSServer.IsServerLocal | Should be $true
}
It "PortNumber should be $($Port)" {
$WSUSServer.PortNumber | Should be "$($Port)"
}
It 'PreferredCulture should be en' {
$WSUSServer.PreferredCulture | Should be 'en'
}
It "ServerName should be $($FQDN)" {
$WSUSServer.ServerName | Should be "$($FQDN)"
}
It 'UseSecureConnection should be True' {
$WSUSServer.UseSecureConnection | Should be 'True'
}
It "WebServiceUrl should be https://$($FQDN):$($Port)/ApiRemoting30/WebService.asmx" {
$WSUSServer.WebServiceUrl | Should be "https://$($FQDN):$($Port)/ApiRemoting30/WebService.asmx"
}
It 'Should use SUSDB' {
$DB.DatabaseName | Should be 'SUSDB'
}
It 'Should use a DB server named' {
$DB.ServerName | Should be 'MICROSOFT##WID'
}
It 'Should use a DB server named' {
$DB.IsUsingWindowsInternalDatabase | Should be $true
}
It 'Should use the Windows Auth mode' {
$DB.AuthenticationMode | Should be 'WindowsAuthentication'
}
It 'Should have data on D:\WSUS.Content\WsusContent' {
$WSUSConfig.LocalContentCachePath | Should be 'D:\WSUS.Content\WsusContent'
}
It 'Should sync from MU' {
$WSUSConfig.SyncFromMicrosoftUpdate | Should be $true
}
It 'Should use a proxy' {
$WSUSConfig.UseProxy | Should be $true
}
It "Proxy Should be set to $($Proxy)" {
$WSUSConfig.ProxyName | Should be "$($Proxy)"
}
It "Proxy port Should be set to $($ProxyPort)" {
$WSUSConfig.ProxyServerPort | Should be "$($ProxyPort)"
}
It 'Proxy use same proxy for SSL ' {
$WSUSConfig.UseSeparateProxyForSsl | Should be $false
}
It 'Should be targeting clients using Groups and GPO' {
$WSUSConfig.TargetingMode | Should be 'Client'
}
}
Context 'Products' {
BeforeAll {
$Categories = (Get-WsusServer).GetSubscription().GetUpdateCategories()
}
It 'Should have the Office 2010 product' {
($Categories| Where Id -eq '84f5f325-30d7-41c4-81d1-87a0e6535b66').Title | Should be 'Office 2010'
}
It 'Should have the Windows 7 product' {
($Categories| Where Id -eq 'bfe5b177-a086-47a0-b102-097e4fa1f807').Title | Should be 'Windows 7'
}
It 'Should have the Windows Defender product' {
($Categories| Where Id -eq '8c3fcc84-7410-4a95-8b89-a166a0190486').Title | Should be 'Windows Defender'
}
}
Context 'Classification' {
BeforeAll {
$Classifications = (Get-WsusServer).GetSubscription().GetUpdateClassifications()
}
It 'Should have enabled the Critical Updates' {
($Classifications | Where Id -eq 'e6cf1350-c01b-414d-a61f-263d14d133b4').Title | Should be 'Critical Updates'
}
It 'Should have enabled the Definition Updates' {
($Classifications | Where Id -eq 'e0789628-ce08-4437-be74-2495b842f43b').Title | Should be 'Definition Updates'
}
It 'Should have enabled the Feature Packs' {
($Classifications | Where Id -eq 'b54e7d24-7add-428f-8b75-90a396fa584f').Title | Should be 'Feature Packs'
}
It 'Should have enabled the Security Updates' {
($Classifications | Where Id -eq '0fa1201d-4330-4fa8-8ae9-b877473b6441').Title | Should be 'Security Updates'
}
It 'Should have enabled the Service Packs' {
($Classifications | Where Id -eq '68c5b0a3-d1a6-4553-ae49-01d3a7827828').Title | Should be 'Service Packs'
}
It 'Should have enabled the Update Rollups' {
($Classifications | Where Id -eq '28bc880e-0592-4cbf-8f95-c79b17911d5f').Title | Should be 'Update Rollups'
}
It 'Should have enabled the Updates' {
($Classifications | Where Id -eq 'cd5ffd1e-e932-4e3a-bf74-18bf0b1bbd83').Title | Should be 'Updates'
}
}
Context 'Languages' {
It 'should only sync English language' {
(Get-WsusServer).GetConfiguration().GetEnabledUpdateLanguages() | Should be 'en'
}
}
Context 'Sync schedule' {
It 'Should sync manually' {
(Get-WsusServer).GetSubscription().SynchronizeAutomatically | Should be $false
}
}
Context 'Auto approval rules' {
It 'Should not have automatic approval rules' {
((Get-WsusServer).GetInstallApprovalRules() | Where Name -eq 'Default Automatic Approval Rule').Enabled | Should be $false
}
}
Context 'Computer target groups' {
It 'Should have a Windows 7 x64 target group' {
((Get-WsusServer).GetComputerTargetGroups() | Where Name -eq 'Windows 7 x64').Name | should be 'Windows 7 x64'
}
}
}

…I’m actually ready to assess the operational readiness of my WSUS configuration by using the following cmdlet:

Invoke-Pester -Script ~/Documents/Pester/Test-WSUS.Tests.ps1

wsus-server-config-invoke-pester

I still feel like a Pester newbie but no doubt that Pester rocks 😎