PowerShell Security Ebook
PowerShell Security Ebook
1. Edition 2020
ISBN: 9798579643858
All rights reserved. No portion of this book may be reproduced in any form
without permission from the publisher, except as permitted by U.S. copy-
right law.
Every effort has been made to ensure that the content provided in this
book is accurate and helpful for our readers at publishing time. However,
this is not an exhaustive treatment of the subjects. No liability is assumed
for losses or damages due to the information provided. You are responsi-
ble for your own choices, actions, and results.
Michael Pietroforte
Wolfgang Sommergut
PowerShell Security
Limit language features, secure communications, track abuse
5.2 Scriptblock logging: Record commands in the event log ...... 106
5.3 Issuing certificates for document encryption ....................... 112
5.4 Encrypt event logs and files with PowerShell and GPO......... 119
8
Lax default configuration of PowerShell
9
Hacking tools for PowerShell
10
Hacking tools for PowerShell
This blog post by Carrie Roberts demonstrates how to outwit most virus
scanners by searching and replacing a few significant code snippets. At this
point, the technique discussed there may not be up to date any more, but
a bit of experimenting will probably reveal how virus scanners detect this
script. Otherwise, various AMSI-Bypasses can help you to overwhelm Win-
dows Defender.
11
General blocking of PowerShell
12
General blocking of PowerShell
tool for most admins. For example, PowerShell logon scripts that are exe-
cuted in the security context of a user will no longer work.
13
Circumvention through alternative shells
14
Secure PowerShell with integrated mechanisms
PowerShell 2.0 is an optional feature starting with Windows 8 and Server 2012 and is ena-
bled by default.
15
Secure PowerShell with integrated mechanisms
Besides the means to prevent the abuse of PowerShell, there are also
functions to track down suspicious and unwanted activities. This includes
the recording of all executed commands in log files (Transcription) as well
as the newer Deep Scriptblock Logging.
16
Secure PowerShell with integrated mechanisms
The latter records all PowerShell actions in the event log. These entries
can be encrypted using Protected Event Logging and thus be protected
from prying eyes. Overall, PowerShell has a number of mechanisms that
make malicious use much more difficult.
The event viewer presents only the encrypted entries, it cannot decode them.
17
Secure PowerShell with integrated mechanisms
App
Event Trans- Dynamic Evalu- Encrypted
Whitelist-
Logging cription ation Logging Logging
ing
JScript No No No No Yes
LUA No No No No No
Perl No No No No No
PHP No No No No No
Python No No No No No
Ruby No No No No No
sh No No No No No
VBScript No No No No Yes
zsh No No No No No
18
Secure PowerShell with integrated mechanisms
Bash No No Yes No
CMD / BAT No No No No
JScript Yes No No No
Python No No No No
Ruby No No No Yes
sh No No Yes No
T-SQL No No No No
VBScript Yes No No No
zsh No No Yes No
19
Setting an execution policy
20
Setting an execution policy
Set-ExecutionPolicy RemoteSigned
then it will fail if you have not opened the PowerShell session with admin-
istrative privileges.
Users without administrative rights cannot change the execution policy for the scope Local-
Machine.
The reason for this lies in the validity area for the execution policy. If the
scope is not explicitly specified, Set-ExecutionPolicy assumes LocalMa-
chine. This would change the setting for all users on this machine, hence
you need admin rights for this.
21
Setting an execution policy
The scope Process, which affects the current session, is even more specific.
The setting for this is not stored in the registry as usual, but in the envi-
ronment variable $env:PSExecutionPolicyPreference. It is discarded at the
end of the session.
The configuration of the execution policy for each scope can be displayed
with:
Get-ExecutionPolicy -List | ft -AutoSize
22
Setting an execution policy
The setting responsible for configuring the execution policy can be found
for the computer and user configuration under Policies => Administrative
Templates => Windows Components => Windows PowerShell and is called
Turn on Script Execution.
The execution policy configured in this way overrides the interactively de-
fined values and also prevents an administrator from changing them on
the command line. A bypass by invoking a new shell with
powershell.exe -ExecutionPolicy "Unrestricted"
23
Setting an execution policy
does not work either, whereas this technique can be used to override a
policy for LocalMachine. Furthermore, resetting to the Undefined value is
only possible by deactivating the GPO.
A group policy can thus be used to specify which criteria scripts must meet
in order to be allowed to run (this policy does not affect logon scripts, by
the way). This prevents untrustworthy scripts from accidentally causing
damage due to settings that are too lax.
24
Signing PowerShell scripts
The first step is to make sure that the certificate template for code signing
is accessible to users who want to request a certificate for their scripts. To
25
Signing PowerShell scripts
Open certificate templates from the MMC tool Certification Authority (certsrv.msc)
26
Signing PowerShell scripts
There you select Properties from the context menu of Code signing and
switch to the Security tab. Next you add the group that should request
certificates based on this template and grant it the Read and Enroll per-
missions.
27
Signing PowerShell scripts
After confirming this dialog, return to certsrv.msc. From the context menu
of certificate templates execute the command New => Certificate Tem-
plate to Issue. In the following dialog you select code signing and close it
with Ok.
28
Signing PowerShell scripts
Now the developer of scripts can go ahead and request a certificate based
on this template. To do this, he starts mmc.exe and adds the snap-in cer-
tificates from the File menu. For users who do not have elevated privi-
leges, the tool automatically opens in the context of Current User.
29
Signing PowerShell scripts
Here you right-click on Personal and then select All Tasks => Request New
Certificate. This starts a wizard where you select the certificate enrollment
policy in the first dialog (usually the default one for AD).
Then you select the template Code Signing, open its details and click on
Properties. In the dialog that appears, enter the necessary data under Sub-
ject and switch to the Private Key tab to check the option Make private key
exportable.
30
Signing PowerShell scripts
Select the code signing template and make the private key exportable
After confirming this dialog, back in the main window click on Register.
Now the result of the operation is displayed and you can complete the
process with Enroll.
31
Signing PowerShell scripts
The certificate can now be found in the user's local store under Personal
=> Certificates. This can be displayed in PowerShell using the correspond-
ing provider:
Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert
You can take advantage of this command used to specify the certificate
when signing the script with Set-AuthenticodeSignature:
Set-AuthenticodeSignature myScript.ps1 `
(dir Cert:\CurrentUser\My -CodeSigningCert)
32
Signing PowerShell scripts
33
Signing PowerShell scripts
When the script is started for the first time on a computer after signing,
the user must confirm the execution if the publisher is not considered to
be trustworthy.
If you select the option Always run, this prompt will not appear in the fu-
ture because the certificate is saved in the store. In this respect, Pow-
erShell behaves just like a web browser or RDP client.
After signing a script, PowerShell will refuse to execute it if you make even
the slightest change to it. The only remedy is to re-sign the script.
34
Signing PowerShell scripts
The same applies when the certificate expires. In this case the script can
also no longer be used. But you can prevent this by using a timestamp
server when signing.
This proves that the certificate was valid at the time of signing.
35
Reduce PowerShell risks with Constrained Language Mode
The ability to instantiate COM and .NET objects or to generate new data
types (with add-type) that have been defined in other languages is partic-
ularly dangerous capability of PowerShell.
The constrained language mode blocks these features (except access to
permitted .NET classes). It also prevents the declaration of classes, usage
of configuration management with DSC, and XAML-based workflows (see
Microsoft Docs for a complete list).
36
Reduce PowerShell risks with Constrained Language Mode
Displaying and changing the Language Mode via the variable $ExecutionContext.Session-
State.LanguageMode
It is obvious that setting this variable does not provide any real protection.
You may not be able to change it back to FullLanguage in the same session,
but a new PowerShell session will again offer the full range of languages
features.
37
Reduce PowerShell risks with Constrained Language Mode
In centrally managed environments you will probably set the system vari-
able using group policies preferences.
38
Reduce PowerShell risks with Constrained Language Mode
39
Reduce PowerShell risks with Constrained Language Mode
The event log shows whether the execution of the test scripts was successful or if it has
failed.
The effect of this mechanism can easily be seen in AppLocker's event log.
AppLocker logs the creation and execution of these test files with the ID
40
Reduce PowerShell risks with Constrained Language Mode
If you use AppLocker for this task, you have to create a new GPO and then
edit it in the GPO editor. There you navigate to Computer Configuration =>
Policies => Windows Settings => Security Settings => Application Control
Policies => AppLocker and follow the link Configure rule enforcement. In
the dialog that appears, you then activate the option Script rules.
41
Reduce PowerShell risks with Constrained Language Mode
Setting the start type for the Application Identity service to automatic
For a central management of this Windows service, the use of Group Pol-
icy is recommended.
42
Reduce PowerShell risks with Constrained Language Mode
Finally it is necessary to define rules that block the start of scripts in the
Temp directory. To do this, simply switch to Script Rules below AppLocker
and select Create Default Rules from the context menu.
They allow standard users to execute scripts only from the Windows or
Program Files directories, i.e. in locations where users cannot store any
files themselves. Administrators are explicitly exempted from this re-
striction by a separate rule.
43
Reduce PowerShell risks with Constrained Language Mode
Here you create a new policy and in the first step you add the extensions
ps1 and psm1 to the list of the designated file types.
44
Reduce PowerShell risks with Constrained Language Mode
Then you create a New Path Rule under Additional Rules. Here you enter
%temp% as the Path and leave the setting for Security level set to Disal-
lowed.
45
Reduce PowerShell risks with Constrained Language Mode
46
Reduce PowerShell risks with Constrained Language Mode
PowerShell 2.0 is an optional feature starting with Windows 8 and Server 2012 and is ena-
bled by default.
It has only been introduced with PowerShell 3.0 and can easily be by-
passed by a hacker switching to an older version. All he needs to do is to
enter the command:
powershell.exe -version 2.0
You can check whether this old version is still activated on a PC by enter-
ing:
Get-WindowsOptionalFeature -Online `
-FeatureName MicrosoftWindowsPowerShellV2
However, you can only uninstall it on Windows 8 and Server 2012 or later,
where PowerShell 2.0 is an optional feature.
47
Installing OpenSSH on Windows 10 and Server 2019
3 Secure communication
3.1 Installing OpenSSH on Windows 10 and
Server 2019
Windows Server 2019 includes OpenSSH as an optional feature for the first
time, thus simplifying installation and configuration. However, errors in
the earlier builds of the operating system prevent a successful activation
of the SSH server. In WSUS environments OpenSSH has the same problems
as RSAT.
The porting of OpenSSH to Windows makes it easier to manage heteroge-
neous environments. Linux computers can be remotely administered via
SSH from Windows, and thanks to the new OpenSSH server, the reverse is
now also possible. In addition, PowerShell Core supports remoting via SSH,
even between different OSes.
One would expect that a system component with such strategic im-
portance is delivered as part of the operating system and can be installed
as a feature via the Server Manager or PowerShell.
However, Microsoft has decided to provide OpenSSH as an optional fea-
ture (also called "Feature on Demand"). This unifies the installation be-
tween client and server OS. The following description therefore also ap-
plies to Windows 10 from Release 1803 onwards.
48
Installing OpenSSH on Windows 10 and Server 2019
To install OpenSSH server, start Settings, then go to Apps => Apps and Fea-
tures => Manage Optional Features. As you can see from the list of in-
stalled components, the SSH client is already installed by default. The
server, on the other hand, you need to add using the Add Features option.
In the list above, select OpenSSH server and click on the Install button that
appears. Windows will now download the required files over the Internet.
If an error occurs, you will not receive a message from the Settings App,
but it will simply jump back to the list of features.
49
Installing OpenSSH on Windows 10 and Server 2019
There are at least two reasons why you may encounter problems here. If
the build of the system is older than 17763.194, then you will see the error
Add-WindowsCapability failed. Error code = 0x800f0950
50
Installing OpenSSH on Windows 10 and Server 2019
The installation of OpenSSH Server fails on earlier builds of Windows Server 2019.
In this case you need a current cumulative update to fix the problem (it is
documented here: bit.ly/3kCi0Pv).
A further hurdle arises if the server, which is usually the case, is updated
via WSUS. Microsoft delivers features on demand bypassing WSUS, so you
don't get them via the internal update server.
Therefore, it is not unlikely that PowerShell will present the following error
here:
Error with "Add-WindowsCapability". Error code: 0x8024002e
51
Installing OpenSSH on Windows 10 and Server 2019
In the eventlog you will then find an entry with ID 1001 stating that the
OpenSSH-Server-Package is not available.
Eventlog entry when adding OpenSSH server as optional component in a WSUS environment
52
Installing OpenSSH on Windows 10 and Server 2019
At the same time, you must ensure that neither the setting Do not connect
to Windows Update Internet locations nor Remove access to use all Win-
dows Update features is in effect.
The latter may have been enabled to prevent users from manually down-
loading feature updates. This primarily affects Windows 10 rather than the
server.
53
Installing OpenSSH on Windows 10 and Server 2019
OpenSSH Server installs two services which are not yet running and whose
startup type is manual and disabled. If you want to use SSH regularly, you
will want to start the services automatically.
Displaying the Startup Type and Status of SSH Services with PowerShell
This can be configured via the GUI services, but the fastest way is using
PowerShell:
Set-Service sshd -StartupType Automatic
To put the SSH server into operation immediately, you must also start the
two services manually:
Start-Service sshd
Start-Service ssh-agent
This command
Get-Service -Name *ssh* |
select DisplayName, Status, StartType
is used to check whether the settings for the two services match and
whether they were started successfully. Now you can check if the firewall
rule for incoming SSH connections has been properly activated:
54
Installing OpenSSH on Windows 10 and Server 2019
If this condition is also fulfilled, then the connection test is good to go.
From a Windows 10 PC or a Linux computer you can connect to the freshly
configured server:
ssh <Name-of-Server>
This will direct you at the old command prompt, but you can also start
PowerShell there.
55
Installing OpenSSH on Windows 10 and Server 2019
Finally, you should consider whether you would like to use public key au-
thentication for security reasons. This also increases user comfort because
you no longer have to enter a password.
56
PowerShell remoting with SSH public key authentication
The first thing you have to do is create the private and the public key,
which you can do by simply running the ssh-keygen command. By default,
57
PowerShell remoting with SSH public key authentication
the command saves the key pair in the .ssh folder in your user profile.
id_rsa is the private key, and id_rsa.pub is the public key.
If you want to work without a passphrase, you can just hit Enter twice.
However, I recommend using a passphrase because if someone gets ac-
cess to your private key, this will compromise all your remote machines.
Thanks to the ssh-agent, you don't have to enter the passphrase whenever
you connect to a remote machine. The ssh-agent runs as a service and
securely stores your private key. At a PowerShell console, you can start the
ssh-agent this way:
Start-Service ssh-agent
If you want the service to start automatically after a restart, you can use
this command:
Set-Service ssh-agent -StartupType Automatic
To add your private key to the ssh-agent, you have to enter this command:
ssh-add <path to private key>
You will have to enter your passphrase here once. After that you can re-
move your private key from the .ssh folder and store it in a safer place.
58
PowerShell remoting with SSH public key authentication
Creating a key pair, adding the private key to the ssh agent and removing it again
If you later want to remove the private key from the ssh-agent, you can do
it with this command:
ssh-add -d ida_rsa
Note that this requires that you provide the SSH key. In case you have lost
your private key, you can remove all private keys from the ssh-agent:
ssh-add -D
Next, you have to copy the contents of the public key file id_rsa.pub to the
remote host. Just paste it to the authorized_keys file in C:\Users\<your
user name\.ssh\.
59
PowerShell remoting with SSH public key authentication
60
PowerShell remoting with SSH public key authentication
to the file and save it. You have to restart the ssh service to apply the
changes. You can do this at a PowerShell console with admin rights:
Restart-Service sshd
You are now back onto your local host and ready to test your connection.
At a PowerShell 6 or 7 console, simply enter this command:
Enter-PSession -HostName <remote host> `
-UserName <user name on the remote computer>
The HostName parameter ensures PowerShell will connect via SSH instead
of WinRM. Note that your user name on the remote computer doesn't
have to be same if you use the UserName parameter. If you omit this pa-
rameter, PowerShell will take your current logon name on the local com-
puter.
Notice you have to enter neither the Windows password nor the pass-
phrase for the private key.
Invoke-Command works in just the same way:
Invoke-Command -HostName <remote hosts> `
-UserName <user name on the remote computer> `
-ScriptBlock {get-process}
61
PowerShell remoting with SSH public key authentication
You can also connect with any SSH client. OpenSSH comes with a simple
SSH client you can launch from the command prompt:
ssh <user name on the remote computer>@<remote host>
Just for the sake of completeness, if you didn't store your private key in
the ssh-agent, you can still work with public key authentication. If the pri-
vate key is located in the .ssh folder of your user profile, OpenSSH will au-
tomatically find the key. If you stored the key in another location, you have
to pass the private key.
With the ssh client you can use the -i parameter:
ssh -i <path to private key>id_rsa <user name on the remote
host>@<remote host>
62
PowerShell remoting with SSH public key authentication
if you use a passphrase, it is more secure to work with the ssh-agent be-
cause you are safe from keyloggers and other password stealing methods.
63
Creating a self-signed certificate
64
Creating a self-signed certificate
This command creates a new certificate under My in the store for the local
machine, with the subject set to "lab.contoso.de".
Using the command
dir Cert:\LocalMachine\my\<thumbprint-of-certificate> |
fl -Property *
you can see that the new certificate has, among other things, the following
default properties:
EnhancedKeyUsageList: {client authentication(1.3.6.1.5.5.7.3.2),
server authentication (1.3.6.1.5.5.7.3.1)}
NotAfter: 22.03.2020 18:52:22
HasPrivateKey: True
Issuer: CN=lab.contoso.de
Subject: CN=lab.contoso.de
65
Creating a self-signed certificate
If the certificate is generated with the default values, it will be suitable for client and server
authentication.
66
Creating a self-signed certificate
Displaying the properties of the new certificate in the MMC certificate snap-in.
The cmdlet issues a SAN certificate when you use the DnsName parame-
ter. There you specify the subject alternative names as a comma-sepa-
rated list. The first of them also serves as the subject as well as the issuer
if you do not use a certificate to sign the new certificate by using the signer
parameter.
You may also specify wildcards following the pattern
New-SelfSignedCertificate -DnsName `
lab.contoso.de, *.contoso.de -cert Cert:\LocalMachine\My
67
Creating a self-signed certificate
You can override most of the defaults for new certificates with your own
parameters for New-SelfSignedCertificate (bit.ly/37c1Plf), but only from
Windows 10 and Server 2016 on. Before that, the cmdlet only accepted
the parameters DnsName, CloneCert und CertStoreLocation.
The following command allows you to extend the validity beyond one year
by specifying a date:
New-SelfSignedCertificate -DnsName lab.contoso.de `
-CertStoreLocation Cert:\LocalMachine\My `
-NotAfter (Get-Date).AddYears(2)
68
Creating a self-signed certificate
If you select 'Custom' as the type, you generate a certificate with all purposes.
If you do not want the private key to be exportable, you can achieve this
using the parameter:
-KeyExportPolicy NonExportable
If you want to export the certificate to a PFX file in order to use it on an IIS
web server, then Export-PfxCertificate serves this purpose. However, it re-
quires that you secure the target file, either with a password or with access
rights that you set using the ProtectTo parameter.
If you use a password, you first turn it into a secure string:
69
Creating a self-signed certificate
You specify the certificate via the path in the store and its thumbprint.
In Windows the name extension for such an export file is usually ".cer".
70
Remoting over HTTPS with a self-signed certificate
71
Remoting over HTTPS with a self-signed certificate
72
Remoting over HTTPS with a self-signed certificate
The second and, in my view, bigger problem is that, if you are working with
machines that are not in an Active Directory domain, you don’t have any
trust relationship with the remote computers. You are then dealing only
with symmetric encryption, so man-in-the-middle attacks are theoretically
possible because the key has to be transferred first.
There you have to add the remote machines that are not in an Active Di-
rectory domain to your TrustedHosts list on the client. However, you don’t
improve security just by defining IP addresses or computer names as trust-
worthy. This is just an extra hurdle that Microsoft added so you know that
you are about to do something risky.
This is where PowerShell Remoting via SSL comes in. For one, HTTPS traffic
is always encrypted. Thus, you can always automate your tasks remotely,
free of worry. And, because SSL uses asymmetric encryption and certifi-
cates, you can be sure that you are securely and directly connected to your
remote machine and not to the computer of an attacker that intercepts
and relays your traffic.
On the downside, configuring PowerShell Remoting for use with SSL is a
bit more difficult than just running Enable-PSRemoting. The main problem
is that you need an SSL certificate. If you just want to manage some stand-
alone servers or workstations, you probably don’t like to acquire a pub-
licly-signed certificate and want to work with a self-signed certificate in-
stead.
73
Remoting over HTTPS with a self-signed certificate
However, you will now see that enabling SSL for WinRM on the client and
on the server is not so difficult (although it is not as straightforward as with
SSH), and you can do it all with PowerShell’s built-in cmdlets. You don’t
even need the notorious winrm Windows command-line tool.
It is important to pass the name of the computer that you want to manage
remotely to the -DnsName parameter. If the computer has a DNS name,
you should use the fully qualified domain name (FQDN).
Issue self-signed certificate, export it, and generate HTTPS listener for PowerShell remoting.
74
Remoting over HTTPS with a self-signed certificate
If you want to, you can verify that the certificate has been stored correctly
using the certificate add-in of the Microsoft Management Console (MMC).
Type mmc on the Start screen and add the Certificates add-in for a com-
puter account and the local computer. The certificate should be in the Per-
sonal\Certificates folder.
75
Remoting over HTTPS with a self-signed certificate
To ensure that nobody uses HTTP to connect to the computer, you can
remove the HTTP listener this way:
Get-ChildItem WSMan:\Localhost\listener |
Where -Property Keys -eq "Transport=HTTP" |
Remove-Item -Recurse
76
Remoting over HTTPS with a self-signed certificate
We are using the $Cert variable that we defined before to read the Thumb-
print, which allows the New-Item cmdlet to locate the certificate in our
certificates store.
The last thing we have to do is configure the firewall on the host because
the Enable-PSRemoting cmdlet only added rules for HTTP:
New-NetFirewallRule -LocalPort 5986 -Protocol TCP `
-DisplayName "Windows Remote Management (HTTPS-In)" `
-Name "Windows Remote Management (HTTPS-In)" -Profile Any
Notice here that we allow inbound traffic on port 5986. WinRM 1.1 (cur-
rent version is 3.0) used the common HTTPS port 443. You can still use this
port if the host is behind a gateway firewall that blocks port 5986:
Set-Item WSMan:\localhost\Service\EnableCompatibility-
HttpsListener -Value true
77
Remoting over HTTPS with a self-signed certificate
Of course, you then have to open port 443 in the Windows Firewall. Note
that this command won’t work if the network connection type on this ma-
chine is set to Public. In this case, you have to change the connection type
to private:
Set-NetConnectionProfile -NetworkCategory Private
For security reasons, you might want to disable the firewall rule for HTTP
that Enable-PSRemoting added:
Disable-NetFirewallRule -DisplayName "Windows Remote Man-
agement (HTTP-In)"
Our remote machine is now ready for PowerShell Remoting via HTTPS, and
we can configure our local computer.
Things are a bit easier here. First, you have to copy the certificate file to
where we exported our certificate. You can then import the certificate
with this command:
Import-Certificate -Filepath "C:\temp\cert" `
-CertStoreLocation "Cert:\LocalMachine\Root"
Note that we need to store the certificate in the Trusted Root Certification
Authorities folder here and not in the Personal folder as we did on the
remote computer. Your computer trusts all machines that can prove their
authenticity with the help of their private keys (stored on the host) and
the certificates stored here.
78
Remoting over HTTPS with a self-signed certificate
By the way, this is why we don’t have to add the remote machine to the
TrustedHosts list. In contrast to PowerShell Remoting over HTTP, we can
be sure that the remote machine is the one it claims to be. This is the main
point of using HTTPS instead of HTTP.
We are now ready to enter a PowerShell session on the remote machine
via HTTPS:
Enter-PSSession -ComputerName myHost `
-UseSSL -Credential (Get-Credential)
In that case you can just add the the -SkipCACheck parameter.
The Invoke-Command cmdlet also supports the -UseSSL parameter:
79
Remoting over HTTPS with a self-signed certificate
3.4.4 Conclusion
HTTPS doesn’t just add another encryption layer; its main purpose is to
verify the authenticity of the remote machine, thereby preventing man-
in-the-middle attacks. Thus, you only need HTTPS if you do PowerShell Re-
moting through an insecure territory. Inside your local network, with trust
relationships between Active Directory domain members, WSMan over
HTTP is secure enough.
80
JEA Session Configuration
By default, this option is not available to standard users and their requests
will be rejected by the target computer. However, if you want to delegate
tasks to employees without administrative privileges, you have to relax
this strict rule.
81
JEA Session Configuration
By default, users without administrative rights cannot establish a remote session with Pow-
erShell.
82
JEA Session Configuration
If you create a new session, such as with Enter-PSSession, and do not spec-
ify a particular configuration, then microsoft.powershell takes effect by de-
fault. As you can see from the command
Get-PSSessionConfiguration
Displaying the existing session configurations and their authorizations with Get-PSSession-
Configuration
Theoretically, you could now simply change the security settings of this
configuration to give access for selected standard users. But you should
refrain from that and maintain a working configuration for admins.
83
JEA Session Configuration
Not much is gained with this command, because the new configuration is
only a copy of microsoft.powershell and does not allow users other than
admins to access the computer. Hence, you should define the permissions
when you create the configuration.
84
JEA Session Configuration
This opens the dialog you already know from managing file permissions.
So far you have already configured who is allowed to start a session on this
remote computer using the new configuration. In addition, you can also
specify under which user ID this should happen by passing the respective
ID to the RunAsCredential parameter:
Register-PSSessionConfiguration -Name HelpDesk `
-RunAsCredential contoso\FLee
85
JEA Session Configuration
Specify the account under which the remote session should run if it was started from session
configuration.
PowerShell then prompts for the password and stores it in the configura-
tion. If a user then connects to the target PC via a session configuration,
he or she will automatically work there in the context of this account. If
you do not use this option, the connection is made under the locally
logged on user.
Working under a different account might give the users different permis-
sions in the file system, but functional restrictions imposed by a session
configuration apply regardless of the account used. The RunsAs account
therefore does not require any permissions in the Security Descriptor of
the session configuration.
The Register-PSSessionConfiguration cmdlet provides several parameters
that can be used to limit the users' options:
86
JEA Session Configuration
87
JEA Session Configuration
The file name requires the .pssc extension. Then open the file in a text
editor and add the desired settings, some of which are already available
and commented out.
88
JEA Session Configuration
To restrict the available cmdlets to those which only read and do not write,
you could use the expression Get*, Select*:
New-PSSessionConfigurationFile -Path .\MyConfig.pssc `
-VisibleCmdlets "Get*","Select*"
If you now try to establish an interactive remote session with the com-
puter, you will fail, because not all necessary commands are available:
Enter-PSSession -ComputerName remote-pc `
-ConfigurationName HelpDesk
89
JEA Session Configuration
90
JEA Session Configuration
As the two commands above show, you have to specify the desired session
configuration using the ConfigurationName parameter. If you don't do
that, microsoft.powershell will be applied and non-administrative users
will be kept out. But you can specify which configuration is used by default
with the variable $PSSessionConfigurationName.
Finally, you can remove session configurations that you no longer need by
using the Unregister-PSSessionConfiguration cmdlet. It requires only the
name of the configuration as its arguments.
91
Defining and assigning role functions
This has at least two advantages. First, you have to update a session con-
figuration every time you change role functions directly in its configuration
file, and then restart WinRM. In contrast, external role definitions are
simply read in at runtime.
Secondly, independent role capability files can be assigned to several ses-
sion configurations, so that redundant information can be avoided. Con-
versely, it is also possible to use several of these role functions in a single
session configuration so that they can be structured modularly.
92
Defining and assigning role functions
The files with the .psrc extension to describe role capabilities are text files.
A skeleton file can be created with the command:
New-PSRoleCapabilityFile -Path MyRCF.psrc
93
Defining and assigning role functions
Selecting the cmdlets that you want to use for a session configuration
If you select a module from the drop-down in the third row and then click
on Filter Cmdlets, the list in the second row is reduced to the cmdlets of
that module. After you have selected a cmdlet, a drop-down menu opens
94
Defining and assigning role functions
next to it with all of its parameters. Here you can select individual param-
eters or mark none of them in order to enable all of them.
The tool offers additional features such as creating a .psrc skeleton with
New-PSRoleCapabilityFile or a new session configuration. Because of the
cumbersome operation, you will usually do without it.
95
Defining and assigning role functions
Once you have created the list of permitted cmdlets and parameters, you
can add them to the .psrc file. You save this file in a directory called
RoleCapabilities under
$env:ProgramFiles\WindowsPowerShell\Modules
The last step is to link the role capabilities to the desired session configu-
ration. To do this, edit the configuration file with the extension .pssc and
add the role functions there.
Since you create this file automatically at the beginning, this (commented
out) section for RoleDefinitions should already be there:
# RoleDefinitions = @{ 'CONTOSO\SqlAdmins' = `
@{ RoleCapabilities = 'SqlAdministration' };
'CONTOSO\SqlManaged' = @{ RoleCapabilityFiles =
'C:\RoleCapability\SqlManaged.psrc' };
'CONTOSO\ServerMonitors' = `
@{ VisibleCmdlets = 'Get-Process' } }
Following the same pattern, you now add your own entry, whereby you
have 3 options, as shown in the example. The last of these defines the
allowed cmdlets directly in the Session Configuration File and is therefore
not applicable if you use a .psrc file.
If you save your .psrc file under the name SqlManaged.psrc in the module
path as described above, the entry could look like this:
96
Defining and assigning role functions
RoleDefinitions = @{ 'contoso\SqlAdmins' = `
@{ RoleCapabilities = 'SqlAdministration' }};
This gives the SqlAdmins group from the contoso domain the role capabil-
ities defined in SqlManaged.psrc.
If you have chosen a different location to save the file, then you have to
proceed as shown in the last entry in the example and enter the name of
the file including the path as value for RoleCapabilityFiles.
Finally, you have to update the session configuration using the following
command:
Set-PSSessionConfiguration -Name MySessionConfig `
-Path .\MyConfig.pssc
97
Log commands in a transcription file
This variant has been around since the early days of PowerShell and, in the
past, could be controlled only explicitly by using the Start-Transcript and
Stop-Transcript cmdlets. To enable automatic recording of the commands,
you had to include the Start-Transcript call in the PowerShell profile.
Not only is this cumbersome if you have to configure many machines in
this way, but it is also relatively easy for an attacker to circumvent this
method. However, explicitly starting and stopping the recording using a
cmdlet can be useful if you include it in your own scripts to see what out-
put they produce.
98
Log commands in a transcription file
Since PowerShell 5, you can turn on transcripts using group policy. The
corresponding setting is called Turn on PowerShell Transcription and can
be found under Policies => Administrative Templates => Windows Compo-
nents => Windows PowerShell.
Enable PowerShell transcripts via GPO. Optionally, specify a separate directory and activate
the timestamp.
99
Log commands in a transcription file
By default, the feature creates a directory in the user's profile for each day
and writes the entries for each session to a separate text file, whose name
consists of "PowerShell_transcript" plus the hostname of the computer
and a random number.
PowerShell creates a separate log file for each session on each computer.
Usually you will want to avoid that users read or even change the contents
of these log files. On the one hand, they may contain sensitive information
such as passwords, on the other hand, the necessary write permission
100
Log commands in a transcription file
would make it easy for an attacker to cover his tracks. Therefore, you have
to prevent users from viewing the files and their contents.
For this purpose, Microsoft recommends restricting the NTFS rights on the
shared directory.
101
Log commands in a transcription file
102
Log commands in a transcription file
103
Log commands in a transcription file
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Pow-
erShellCore\Transcription]
"EnableTranscripting"=dword:00000001
"OutputDirectory"="\\server\\pslogs"
104
Log commands in a transcription file
The policies are largely identical to those for Windows PowerShell, and the
same is true for Turn on PowerShell Transcription. It is particularly useful
that each setting has the option Use Windows PowerShell Policy setting so
that you don't have to manage PowerShell 7 separately.
105
Scriptblock logging: Record commands in the event log
While transcriptions can also be explicitly turned on and off using the
Start-Transcript and Stop-Transcript cmdlets, you can enable script block
106
Scriptblock logging: Record commands in the event log
logging only by using GPOs or by setting the appropriate registry key di-
rectly. Therefore, there is still a need for the older method, such as record-
ing the output in your own scripts.
The relevant GPO setting is called Turn on PowerShell Script Block Logging
and can be found under Policies > Administrative Templates > Windows
Components > Windows PowerShell. If you configure it under Computer
and User Configuration, the former setting prevails.
If you select the option for start/stop, then you should expect a consider-
ably higher data volume because markers for the start and stop of all
events will be written to the log.
107
Scriptblock logging: Record commands in the event log
While you prepare the logging in text files by creating a directory on a file
share and assigning the necessary access rights, different preparatory
work is required for the newer logging.
Start by changing the maximum size of the event log from the default of
20 MB to a significantly higher value. This is required for two reasons: First,
depending on the configuration of the logging feature, a relatively large
amount of data is accumulated. Second, attackers should not be able to
simply cover their tracks by filling up the log relatively quickly with unsus-
picious entries.
Since the evaluation of the logs is left either to scripts developed for this
purpose or to SIEM tools, the recorded events are needed at a central lo-
cation. For this purpose, forward the entries written by PowerShell to a
computer in the network.
The logging takes place in the application log under Microsoft=> Windows
=> PowerShell => Operational, and the commands are recorded under
108
Scriptblock logging: Record commands in the event log
event ID 4104. If you also record start and stop events, these appear under
the IDs 4105 and 4106.
In addition, select Warning as the event type and enter 4104 as the ID.
109
Scriptblock logging: Record commands in the event log
While transcripts can write their data to a text file with virtually no limits,
the script block field in the event log limits the length of the record. There-
fore, longer scripts are split up and span several entries.
On Microsoft Docs, there is a template for a PowerShell script that can be
used to reassemble the log fragments. If for example you want to string
together all recordings for a process with ID 6524, then you could proceed
as follows:
$created = Get-WinEvent -FilterHashtable `
@{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} |
where ProcessId -eq 6524
As with transcripts, group policy enables logging of script blocks only for
Windows PowerShell. It has no effect on PowerShell Core 6.x and its suc-
cessor, PowerShell 7.
If you want to record the commands for version 6.x in the event log, you
have to set the registry key yourself. To do this, create the ScriptBlockLog-
ging key under
HKLM\SOFTWARE\Policies\Microsoft\PowerShellCore
and assign the value 1 to EnableScriptBlockLogging.
The following instructions in a .reg file will accomplish this task:
110
Scriptblock logging: Record commands in the event log
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Pow-
erShellCore\ScriptBlockLogging] "EnableScriptBlockLog-
ging"=dword:00000001
PowerShell 7, on the other hand, includes its own ADMX template, which
you can copy to %systemroot%\policydefinitions or to the central store. It
contains all the settings known from PowerShell 5, including those for
scriptblock logging.
Finally, it should be noted that the log entries for PowerShell Core are lo-
cated directly under the Applications and Services logs. The event IDs for
logging are the same as for Windows PowerShell.
111
Issuing certificates for document encryption
[Strings]
szOID_ENHANCED_KEY_USAGE = "2.5.29.37"
szOID_DOCUMENT_ENCRYPTION = "1.3.6.1.4.1.311.80.1"
[NewRequest]
Subject = "cn=me@somewhere.com"
MachineKeySet = false
KeyLength = 2048
KeySpec = AT_KEYEXCHANGE
HashAlgorithm = Sha1
Exportable = true
RequestType = Cert
112
Issuing certificates for document encryption
KeyUsage = "CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERT_DATA_ENCIPHERMENT_KEY_USAGE"
ValidityPeriod = "Years"
ValidityPeriodUnits = "1000"
[Extensions]
%szOID_ENHANCED_KEY_USAGE% = "{text}%szOID_DOCUMENT_EN-
CRYPTION%"
113
Issuing certificates for document encryption
Duplicate an existing template as a basis for the new template for document encryption
Then assign the name for the new template under the General tab and
determine the template's period of validity.
114
Issuing certificates for document encryption
As with the .inf file shown above, the key length should be at least 2048
bits; the corresponding setting is found on the Cryptography tab.
Configure the necessary settings on the Extensions tab. Here, we edit the
Application Policies and remove all existing entries. Instead, we add Docu-
ment Encryption.
115
Issuing certificates for document encryption
116
Issuing certificates for document encryption
Finally, use the Security tab to make sure all users who request a certificate
based on this template have the Read and Register permissions.
Now you can request your certificate using certmgr.msc. If you can't find
your new template in the list or it has a status of Unavailable in the ex-
tended view, then try this troubleshooting tip.
In the details, enter the subject name in the format specified in the tem-
plate. Under Private key => Key options, make sure it is exportable, if re-
quired.
117
Issuing certificates for document encryption
After you click Enroll, the new certificate should appear in the store of the
Current User.
118
Encrypt event logs and files with PowerShell and GPO
119
Encrypt event logs and files with PowerShell and GPO
The encryption of PowerShell entries in the event log can be enabled via group policies
Once the GPO is in effect, you can no longer read the event log history of
the PowerShell commands entered on those machines. However, the
120
Encrypt event logs and files with PowerShell and GPO
Event Viewer lacks the necessary functions to decode the logs using the
private key.
The Event Viewer only presents the encrypted entries; it cannot decode them
Therefore, you must make these log entries readable with PowerShell. The
Unprotect-CmsMessage cmdlet, the opposite of Protect-CmsMessage, de-
crypts them.
For example, if you want to decipher the latest entry in the PowerShell log,
you could retrieve it via Get-WinEvent and pipe it to Unprotect-CmsMes-
sage:
$msg = Get-WinEvent `
Microsoft-Windows-PowerShell/Operational `
-ComputerName myPC -MaxEvents 2 -Credential domain\user
121
Encrypt event logs and files with PowerShell and GPO
A complete script for this purpose can be found on Emin Atac's blog
(bit.ly/2DM85Gx)
The problem with script block logging is that longer command sequences
are split across multiple log entries. Therefore, in this case you would have
to aggregate the individual sections and then pass them to Unprotect-
CmsMessage.
122
Encrypt event logs and files with PowerShell and GPO
Usage scenarios here may also include protecting sensitive data in scripts
or password files against unauthorized access. However, this technology
is certainly not intended as an alternative to an encrypting file system or
even a Bitlocker.
Because PowerShell uses the cryptographic message syntax standard, you
can decrypt encoded files using other tools on different platforms, such as
OpenSSL on Linux (bit.ly/2E21l6Y). Therefore, this PowerShell feature is
also suitable for exchanging confidential data between different operating
systems.
The process is relatively simple. Protect-CmsMessage expects the input file
via the Path parameter. Alternatively, you can provide the contents to be
encrypted via the Content parameter or via a pipeline. The target file is
specified via OutFile; otherwise, the output is stdout.
Other required information includes the certificate you want to use. The
parameter To, which accepts the fingerprint, subject name, or path to a
certificate, serves this purpose.
123
Encrypt event logs and files with PowerShell and GPO
Watch out for the character encoding of files. Otherwise, you will be sur-
prised by a distorted result after decryption. This is the case, for example,
with the following procedure:
Get-Process > process.txt
124
Encrypt event logs and files with PowerShell and GPO
If you prefer the first variant with redirection to a file, then you must con-
vert the content to the correct character set when it is read for encryption:
Get-Content -Raw -Encoding UTF8 process.txt |
Protect-CmsMessage -To "CN=Max White" -out .\process.enc
With this variant, you can take advantage of the appropriate features of
Get-Content.
125
Audit PowerShell keys in the registry
To find out about such manipulations, you should monitor the relevant
keys in the registry. In our example, these would be those set by Group
Policy Objects (GPOs) for PowerShell. As with auditing the file system,
three measures are required:
Enable registry monitoring via GPO
Configure the system access control list (SACL) for the resource in
question
Analyze the event log
The first step is to create a GPO and link it to the organizational unit (OU)
whose machines you wish to monitor for changes to the PowerShell keys
in the registry.
126
Audit PowerShell keys in the registry
Next, open the new policy in the GPO editor and navigate to Computer
Configuration => Policies => Windows Settings => Security Settings => Ad-
vanced Audit Policy Configuration => Audit Policies => Object Access. (Mi-
crosoft has deprecated the settings under Security Settings => Local Poli-
cies => Audit Policy since Windows 7.)
There you activate the Audit Registry setting, where you see two options:
Success and Failure. Deciding whether you want to record failed, success-
ful, or both accesses depends on the type and importance of the resource.
However, you should find a balance between the relevance of the rec-
orded events and the amount of data generated.
127
Audit PowerShell keys in the registry
In our example, we limit ourselves only to Success to find out when the
value of a key actually changed. Executing this command on the target
computers activates the group policy:
gpupdate /force
And now you can customize the SACL for the registry key.
128
Audit PowerShell keys in the registry
Here you add a new entry. First, choose a security principle for tracking,
such as Everyone. In the next step, define which activities to record. For
our purpose, we select Query Value, Set Value, and Delete to record that
a value for this key has changed.
Again, you should keep in mind that monitoring full access may generate
too much data, especially if you configure the SACL further up in the reg-
istry tree.
When changing the SACL of this key in the registry of many computers, it
makes sense to use a GPO. You can configure the necessary setting under
Computer Configuration => Policies => Windows Settings => Security Set-
tings => Registry.
129
Audit PowerShell keys in the registry
There you open the context menu of the container or right-click in the
right panel. Then execute the Add Key command. In the following dialog,
navigate through the registry until you reach the desired key. If this key
does not exist on the local machine, you may also type the path into the
input field.
You can also change the SACL of a registry key via a GPO
After selecting a key, the same security dialog opens as described above
for regedit.exe. Therefore, the following procedure is the same as for con-
figuring the SACL in the registry editor.
Finally, you should monitor the entries in the event log to discover suspi-
cious activities. Find these in the Security protocol with the IDs 4656, 4657,
130
Audit PowerShell keys in the registry
4660, and 4663. As we are only interested in changes in this specific case,
the Event IDs 4657 and 4660 are sufficient. ID 4660 represents deletion.
You can retrieve these logs with PowerShell as follows:
Get-EventLog -LogName Security -Source "*auditing*" -
InstanceId 4657,4660
If you prefer a GUI, you can create a user-defined view in the Event Viewer.
131
Audit PowerShell keys in the registry
Set up a custom view in the Event Viewer to filter out audit logs for registration
132
Avoiding errors using strict mode
Perl has known such a mechanism for a long time, and in VBScript you can
use Option Explicit to force variables to be declared before they are used
for the first time. However, this mechanism doesn't overly limit developers
and require them for example to declare data types.
While in Perl you can enable strict mode separately for variables, subs and
references, PowerShell only expects a version number or the value Off.
You pass the version number to the Set-StrictMode cmdlet.
133
Avoiding errors using strict mode
if( $a -gt 5 ){
Out-Host '$a is greater than 5'
}
134
Avoiding errors using strict mode
When Strict Mode Version 2.0 is activated, this command would issue an
error message for all directories because they do not have a Length prop-
erty.
This is also where the ambivalent nature of this mode becomes apparent,
because it triggers alarms even in harmless cases. Without Strict Mode the
directories would simply not be displayed.
Rather than avoiding strict mode 2, you would have to program more de-
fensively in this example. You could filter out the directories using the
PSIsContainer property:
gci |
? {$_.PSIsContainer -eq $false -and $_.length -gt 1GB}
135
Avoiding errors using strict mode
Strict mode 2.0 also helps to avoid wrong function calls. The different syn-
tax for executing methods and functions is one of the most popular pitfalls
in PowerShell, especially for those users who often deal with other pro-
gramming languages.
The command
myfunc(1, 2, 3)
Finally, there is version 3.0 of Strict Mode, but it is not documented. You
will get it automatically when you invoke
Set-StrictMode -Version Latest
in PowerShell 3.0 or a higher version. But you can also specify the "3.0"
explicitly here.
In addition to the criteria of the other two versions, it also checks whether
elements of an array are retrieved with an invalid index. This can happen
relatively easily if you iterate over the elements of an array in a loop:
# At least PowerShell 3.0
$array = (1,2,3)
# No error, output of $null
Set-StrictMode -Version 2.0
for ($i= 0; $i -le 3; $i++){
$array[$i]
}
136
Avoiding errors using strict mode
# Error IndexOutOfRangeException
Set-StrictMode -Version 3.0
for ($i= 0; $i -le 3; $i++){
$array[$i]
}
and this would also reference $array[3]. With only 3 elements, the highest
index is 2. Hence, Strict Mode 3.0 also acts as a bounds checker. Without
it, PowerShell would output the value $null here.
Finally, it should be noted that the definition of strict mode only applies to
the respective scope and all its included scopes.
137
Avoiding errors using strict mode
The strict mode defined in the function does not apply to calls on the command line.
If you set strict mode to version 3.0 in a function, for example, the default
setting remains on the console, i.e. switched off. Conversely, entering
Set-StrictMode -Version 3.0
on the command line will result in PowerShell checking all scripts started
from there to see whether the array index is out of bounds.
138
Checking code with ScriptAnalyzer
If you develop PowerShell scripts not in VSCode, but in the ISE, as most
admins will probably do, then you can start the code checker from the
command line. To do so you have to install the module from the PSGallery
first:
Install-Module -Name PSScriptAnalyzer
As the command
Get-Command -Module PSScriptAnalyzer
139
Checking code with ScriptAnalyzer
The first of these cmdlets is used to display the available rules against
which the code of scripts is compared. If you call it without parameters, it
will show all of the currently 55 standard rules including their descriptions.
A useful parameter is Severity, which can use the Error and Warning values
to limit the list to serious or less serious problems:
Get-ScriptAnalyzerRule -Severity Error
This command would only show rules where a violation would be classified
as a bug. You need an overview of the rules set if you want to consider
only certain recommendations or exclude others during the review.
140
Checking code with ScriptAnalyzer
141
Checking code with ScriptAnalyzer
A new option in version 1.17.1 is Fix, which can automatically correct cer-
tain deviations from the commonly used rules. This applies, for example,
to the use of aliases for cmdlets. In some cases, script authors have to edit
such corrections manually, for example when converting plain text to a
secure string.
If you only want to check a code fragment and not an entire script file, use
the ScriptDefinition parameter instead of Path and pass the code to it as a
value. In this case, the Fix switch is not available for obvious reasons.
Finally, Invoke-Formatter is the third cmdlet that comes with the module.
As the name suggests, script authors can use it to tidy up the formatting
of the code. There are several conventions to choose from, which can be
selected via the Settings parameter using auto-completion.
142
Checking code with ScriptAnalyzer
143