Remote Powershell Quest

Our goal is to:

  • open from a machine (the client)
  • a Powershell session on a remote machine (the server)
  • using certificates
  • over a SSL connection

On any Windows machine

Create a self signed certificate

This certificate will have a private and a public key.
We’ll pair the public key on the server to a local user.

# Set infos for the certificate

$certuser = "leopardb"
$certuserUPN = "leopardb@mail.cool"

# Instead of generating a file, the cert will be added to the 
# Current User folder in the certificate store
$cert = New-SelfSignedCertificate `
			-Type Custom `
			-Subject "CN=$certuser" `
			-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2","2.5.29.17={text}upn=$certuserUPN") `
			-KeyUsage DigitalSignature `
			-KeyAlgorithm RSA `
			-KeyLength 2048 `
			-CertStoreLocation "Cert:\CurrentUser\My"
PS C:\Windows\system32> Get-ChildItem -Path cert:\CurrentUser\My


   PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint                                Subject
----------                                -------
85F7C77B16893A5502B387A3B4DCEE313DA3CA6E  CN=leopardb

Export the keys

We export the certificate in three different forms:

  • PEM (only public key)
  • PFX (PKCS #12 contains private and public key)
  • CER (contains the public key)
# We set the filenames for the certificate

$certuser = "leopardb"
$output_path = "C:\temp"

# Export the public key
$pem_output = @()
$pem_output += "-----BEGIN CERTIFICATE-----"
$pem_output += [System.Convert]::ToBase64String($cert.RawData) -replace ".{64}", "$&`n"
$pem_output += "-----END CERTIFICATE-----"
[System.IO.File]::WriteAllLines("$output_path\$certuser.pem", $pem_output)

# Export the private key in a PFX file will be converted to private pem with openssl
[System.IO.File]::WriteAllBytes("$output_path\$certuser.pfx", $cert.Export("Pfx"))

# Export the public key in .cer format for logging on other windows machines
Export-Certificate -Cert $cert -FilePath "$output_path\$certuser.cer" -Type Cert

On the server

The following steps must be done with administrator privileges.

First, we enable remote access:

PS C:\Windows\system32> Enable-PSRemoting
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.
WinRM has been updated for remote management.
WinRM firewall exception enabled.
Configured LocalAccountTokenFilterPolicy to grant administrative rights remotely to local users.

Then we allow certificate authentication:

Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true

We open the firewall for incoming WinRM connections

netsh advfirewall firewall add rule name="WinRM-HTTPS" dir=in localport=5986 protocol=TCP action=allow

We create a HTTPS listener with a another self-signed certificate

$computername = ([System.Net.Dns]::GetHostByName(($env:computerName)).Hostname)

$c = New-SelfSignedCertificate `
-DnsName $computername `
-CertStoreLocation cert:\LocalMachine\My `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")

winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$computername`";CertificateThumbprint=`"$($c.ThumbPrint)`"}"

Certificates

If we are using user authentication, the public key of the certificate we are using to connect to the server must be saved in the LocalMachine\TrustedPeople store.

If we are using server authentication, the public key of the certificate we are using to connect to the server must be saved in the LocalMachine\TrustedDevices store.

Also, if our certificate is self-signed, the server must trust it, therefore it must also be present in the LocalMachine\Root store.

So we install our self-signed certificate on the machine:

Import-Certificate -FilePath "C:\cert.cer" -CertStoreLocation cert:\LocalMachine\root
Import-Certificate -FilePath "C:\cert.cer" -CertStoreLocation cert:\LocalMachine\TrustedPeople

At last, we map the imported certificate to a local user:

$certuser = "leopardb"
$certuserUPN = "leopardb@mail.cool"
$localuser = "leo"

$thumbprint = (Get-ChildItem cert:\LocalMachine\TrustedPeople | Where-Object {$_.Subject -match $certuser}).Thumbprint

New-Item -Path WSMan:\localhost\ClientCertificate `
         -Subject $certuserUPN `
         -URI * `
         -Issuer $thumbprint `
         -Credential (Get-Credential $localuser) `
         -Force

Connect from a client

When using certificates for WinRM authentication, the certificate must include the private key and must be put:

  • In the CurrentUser\My store if it’s a user certificate
  • In the LocalMachine\My store if it’s a server certificate

To connect with the certificate (SSL)

$remotecomputer = "DESKTOP-AA7J9T7.leodom.local"
$certusername = "leopardb"
$thumbprint = (Get-ChildItem cert:\CurrentUser\My | Where-Object {$_.Subject -match $certusername}).Thumbprint
$SessionOptions = New-PSSessionOption –SkipCACheck –SkipCNCheck

Enter-PSSession `
	-ComputerName $remotecomputer `
	-SessionOption $SessionOptions `
	-UseSSL `
	-CertificateThumbprint $thumbprint

To connect directly with credentials (SSL)

$remotecomputer = "DESKTOP-AA7J9T7.leodom.local"
$remoteuser = "toto"
$SessionOptions = New-PSSessionOption –SkipCACheck –SkipCNCheck

Enter-PSSession `
-ComputerName $remotecomputer `
-SessionOption $SessionOptions `
-UseSSL `
-Credential (Get-Credential $remotecomputer\$remoteuser)

To connect directly with credentials

$remotecomputer = "DESKTOP-AA7J9T7.leodom.local"
$remoteuser = "toto"

Enter-PSSession `
-ComputerName $remotecomputer  `
-Credential (Get-Credential $remotecomputer\$remoteuser)

Scripts

Open command prompt as admin and run:

powershell.exe -noprofile -executionpolicy bypass -file \\path\to\SetRemotePSWithCert.ps1

SetRemotePSWithCert.ps1:

# Variables for the script

$computername = ([System.Net.Dns]::GetHostByName(($env:computerName)).Hostname)
$certpath = "\\folder\where\the\cert\is"
$certuser = "leopardb"
$certuserUPN = "leopardb@mail.cool"
$localuser = "toto"

# Sadly this one is required

Enable-PSRemoting -SkipNetworkProfileCheck -Force

# Create Server Auth certificate

$c = New-SelfSignedCertificate `
-DnsName $computername `
-CertStoreLocation cert:\LocalMachine\My `
-TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1")

# Create a WinRM HTTPS listener

winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=`"$computername`";CertificateThumbprint=`"$($c.ThumbPrint)`"}"

# Create a firewall rule to allow incoming HTTPS WinRM connections

netsh advfirewall firewall add rule name="WinRM-HTTPS" dir=in localport=5986 protocol=TCP action=allow

# Allow Certificate authentication (default is disabled)

Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true

# Import the certificate

Import-Certificate -FilePath "$certpath\$certuser.cer" -CertStoreLocation cert:\LocalMachine\root
Import-Certificate -FilePath "$certpath\$certuser.cer" -CertStoreLocation cert:\LocalMachine\TrustedPeople

# Map the certificate identified by $username to the local user $localuser

$thumbprint = (Get-ChildItem cert:\LocalMachine\TrustedPeople | Where-Object {$_.Subject -match $certuser}).Thumbprint

New-Item -Path WSMan:\localhost\ClientCertificate `
         -Subject $certuserUPN `
         -URI * `
         -Issuer $thumbprint `
         -Credential (Get-Credential $localuser) `
         -Force

When not in a domain

In a domain

Create GPOs using gpmc.msc

WinRM

Computer Configuration

Policies – Windows Settings – Security Settings – Windows Firewall with Advanced Security – Inbound Rules
Add predefined rule : “Windows Remote Management (HTTP-In)” for Domain,Private (can remove private afterwards)

Policies – Administrative Templates – Windows Components – Windows Remote Management (WinRM) – WinRM Service
“Allow remote server management through WinRM” Enabled, IPv4 Filter: *
if not a * wildcard, then it must be a range even if it’s a single address (ex: 192.168.2.51-192.168.2.51)
this is the Server’s IPs where the Service is listening ! Not the clients ! This filtering is made by the firewall

Preferences – Control Panel Settings – Services
New service
– Startup: Automatic
– Service name: WinRM
– Service action: Start service

WMI

Policies – Windows Settings – Security Settings – Windows Firewall with Advanced Security – Inbound Rules
– Add predefined rule : “Remote Scheduled Tasks Management”
– Add predefined rule : “Windows Management Instrumentation (WMI)”

SNMP

Firewall
Service
Description

Connect from the DC

Enter-PSSession -Computername DESKTOP-AA7J9T7.leodom.local