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
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.
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.
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.
For PowerShell script block logging, check the Log Script Block invocation start/ stop events checkbox
For transcription block logging, I have it configured as such (outputs will be sent to C:\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
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
- https://www.blackhillsinfosec.com/new-powershell-history-defense-evasion-technique
- https://learn.microsoft.com/en-us/powershell/module/psreadline/about/about_psreadline?view=powershell-5.1#command-history
- https://twitter.com/merill/status/1541634611893383168 (way of formatting the output)
- https://4sysops.com/archives/getting-started-with-the-psreadline-module-for-powershell/ (check here if you are curious about PSReadLine itself)
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.