5 min read

Hide your PowerShell commands via PSReadLine

Hide your PowerShell commands via PSReadLine

Overview

Powershell PSReadLine provides several cmdlets that enhances productivity. One of its interesting features is the ability to quickly retrieve the history of commands executed. Starting with PSReadLine v.2.0.4+, sensitive "keywords" included in commands will be not logged. From an offensive perspective, this provides an interesting route to hide commands executed from the prying eyes. This post dives into how you can explore this capability yourself - enjoy!

There are certain requirements that need to be met to get this technique to work. Keep in mind that this only works starting with PSReadLine v.2.0.4+. The following chart was taken from Microsoft's documentation and shows the default versions deployed per PowerShell version.

  • PowerShell 7.3 ships with PSReadLine 2.2.6
  • PowerShell 7.2.5 ships with PSReadLine 2.1.0
  • PowerShell 7.0.11 ships with PSReadLine 2.0.4
  • PowerShell 5.1 ships with PSReadLine 2.0.0
Table of contents

Setup of Settings

Before we start, let us review what PowerShell settings I have enabled. You will need to edit your group policy settings in order to enable module logging, script block logging, and PowerShell transcription. This can be done by either running local group policy editor or start > run > gpedit.msc. These settings were enabled in order to provide multiple sources of logging information.

Navigating to the local group policy editor

once you see the group policy setting, you'll want to click Administrative Templates > Windows Components > Windows PowerShell. Simply change the settings for module logging, PowerShell script block logging, and PowerShell transcription to Enabled.

Red arrows show the subdirectories of interest

For the PowerShell module logging, click on the Show button next to the Module Names and type in * (an asterisk). This means this setting will apply to all modules.

adding the asterisk under the value column

For PowerShell script block logging, check the Log Script Block invocation start/ stop events checkbox

enabling script block logging

For transcription block logging, I have it configured as such (outputs will be sent to C:\Logging directory)

transcription outputs will be stored in the logging directory

Issue a command in PowerShell and verify that your actions are being logged. As you can see from the image below, everything seems to be working :D

window on the bottom left is the transcription block output. window on the right shows the same information from the windows event viewer

Bypass Logging

According to Microsoft, a command will not be logged if it contains the following key words:

  • password
  • asplaintext
  • token
  • apikey
  • secret

It's important to point out that the most common version of PSReadLine within an environment is most likely going to be version 2.0.0. This technique only works starting with version 2.0.4+. Before we start, we need to figure out our PSReadLine version. We can achieve this by issuing the following command

get-module -ListAvailable | where-object -property Name -EQ 'PSReadline'

As you can see from my instance, my version is not 2.0.4

There are a few things you need to do to get this to work. If we navigate to the PSReadLine github repo (https://github.com/PowerShell/PSReadLine), we will see that we need to update our PowerShellGet module. We can see what version we have via the following command.

Install PowerShellGet via: Install-Module -Name PowerShellGet -Force

Once that is complete, execute the following commands

Set-ExecutionPolicy Unrestricted
Import-Module PowerShellGet
powershell -noprofile -command "Install-Module PSReadLine -Force -SkipPublisherCheck -AllowPrerelease"

The last line was chosen as the official documentation states:

You'll need to keep this in mind depending on your target workstation.

A final sanity check reveals that we indeed to do have the correct version as defined in Microsoft's documentation.

The image above is showing that I have two different module versions for PSReadLine. The older version will be used to show what happens when the filtering is not enabled. To utilize the new PSReadLine version, open a new PowerShell instance and execute the following:

Import-Module -FullyQualifiedName @{ModuleName = 'PSReadline'; ModuleVersion = '2.2.6' }

What we see below is something very interesting - the image on the left shows the commands of interest that were executed under the old version of the PSReadLine module. The red box on the right side shows the same set of commands being executed BUT notice down below that the line containing $password is omitted. Interesting :)

The following screenshot shows the same behavior in event viewer and the output transcription block.

Put it into practice

So assuming that the conditions are met, an attacker can leverage this by emplacing those "keywords" throughout their payload or use it for sets of commands they do not want logged. For instance, let us try an AMSI bypass and see if it pops up in the logs :D. In the following image, I demonstrate AMSI blocking the first command (static signature detection). The second invocation is showing that AMSI is patched. Observe how one of the keywords is used on each line.

When we double check the logging output, observe that there is no reference to anything we just executed :D

Summary

  • PSReadLine v.2.0.4 does not log commands that contain certain keywords
  • This behavior can be exploited to omit commands from powershell script block logging and console history.
  • Your experience may vary depending on the target's PowerShell and PSReadLine version

Sources

Disclosure: I am not responsible for what you do with this information. I do not support illegal cybersecurity activities. The information on this blog is presented so that "white hat hackers" (i.e., the good folks) can better understand their craft (whilst enhancing mine - blue teamers, you are not forgotten!). You should never attempt any of the techniques/snippets listed here without explicit permission from the target organization. I will not respond to any messages whose underlying purpose is of malicious intent.