0x00 Preface

---

During my recent studies, I discovered that PowerShell command history sometimes contains sensitive system information, such as connection credentials for remote servers. Therefore, I conducted further research on PowerShell's history functionality, summarized common methods for exporting history in penetration testing, combined with exploitation ideas, and provided defense recommendations.

0x01 Introduction

---

This article will cover the following topics:

  • Two types of PowerShell command history
  • Methods for exporting PowerShell command history
  • Defense recommendations

0x02 Two Types of PowerShell Command History

---

There are two ways to record PowerShell command history, which can be read using Get-History and Get-PSReadlineOption respectively.

1. Get-History

Reference documentation:

https://docs.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Core/Get-History?view=powershell-3.0

Default support for PowerShell v2 and above

Records commands entered in the current session, not shared between multiple PowerShell processes, all records are automatically cleared after the PowerShell process exits

1. Common Commands

Get complete information of history records:

Get-History | Format-List -Property *

Includes:

  • Id
  • CommandLine
  • ExecutionStatus
  • StartExecutionTime
  • EndExecutionTime

Test as shown in the figure below

Alt text

Delete all history records:

Clear-History

Delete command by ID:

Clear-History -Id 3

2. Exploitation Approach

Gained access to a Windows system, discovered a PowerShell process running in the background, and wanted to read the command history from the PowerShell process

(1) PowerShell process cannot receive keyboard input commands

For example, PowerShell loaded a script running in the background: PowerShell -ep bypass -f 1.ps1

In this case, keyboard messages cannot be sent to the PowerShell process. Useful information can be obtained by reading the command-line arguments of the process. Open-source code:

An open-source project

The code implements reading command-line arguments of a specified process, often yielding useful information

(2) PowerShell process can receive keyboard input commands

Here, keyboard messages can be simulated to export the command history

Program implementation approach:

  • By traversing and enumerating all windows
  • Obtain PID from window (HWND) via GetWindowThreadProcessId
  • Compare PIDs to find the qualifying window
  • Send keyboard messages (PostMessage) to the qualifying window

Program details:

1. Virtual-Key Codes

Each keyboard input message corresponds to a Virtual-Key Code

Reference:

https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes

Need to simulate both key press and key release operations, open-source test code:

An open-source project

The code implements searching for a process with a specified PID, sending keyboard messages to the process, with the content: whoami

2. Export history records

Command as follows:

Get-History|export-csv $env:temp"\history.csv"

Special characters such as "|", "$", and """ need to be considered; the Shift key must be pressed when simulating keyboard input

The implementation method here is to first use keybd_event to press the Shift key, then use PostMessage to send the key letters, and finally release both keys

Open source test code:

An open-source project).cpp

The code implements searching for a process with a specified PID and sending keyboard messages to the process, with the content: Get-History|export-csv $env:temp"\history.csv"

3. Additional: View cmd.exe history

Command as follows:

doskey /h

Clear:

doskey /reinstall

It is also possible to export cmd.exe command history by sending keyboard messages

2. Get-PSReadlineOption

Reference documentation:

https://docs.microsoft.com/en-us/powershell/module/psreadline/?view=powershell-5.1

Default support for PowerShell v5

PowerShell v3 and PowerShell v4 require installation of Get-PSReadlineOption before use

After installation, all PowerShell command history is saved in the same location and can be viewed at any time

1. Installation and Usage of PowerShell v3 and PowerShell v4

Taking a 64-bit system as an example, the installation method is as follows:

(1) Install PowerShellGet

Download:

https://www.microsoft.com/en-us/download/details.aspx?id=51451

Note:

The PowerShell process must be closed before installation.

Stealth installation can be achieved via command line with the following command:

msiexec /q /i PackageManagement_x64.msi

After successful installation, it will appear in the installed programs list (Control Panel\Programs\Programs and Features) as: Package Management Preview - x64

It can be hidden by deleting the corresponding registry entry. For more details, refer to 'Penetration Basics - Obtaining the List of Installed Programs on the Current System'.

The registry path for Package Management Preview - x64 is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{57E5A8BB-41EB-4F09-B332-B535C5954A28}

Simply delete this registry key and its subkeys to hide it from the installed programs list.

CMD command to delete the registry key:

reg delete HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{57E5A8BB-41EB-4F09-B332-B535C5954A28} /f

(2) Install PSReadLine

Install via Install-Module command

Install-Module -Name PSReadLine

Prompt appears:

NuGet provider is required to continue
PowerShellGet requires NuGet provider version '2.8.5.201' or newer to interact
with NuGet-based repositories. The NuGet provider must be available in
'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\Administrator\AppData\Local\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running 'Install-PackageProvider
-Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to
install and import the NuGet provider now?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):

Need to enter Y again for installation

To achieve one-click installation, you can first install NuGet, then install PSReadLine. The complete commands are as follows:

Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name PSReadLine

(3) Usage

All PowerShell commands will be saved at a fixed location: %appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

View command history:

Get-Content (Get-PSReadlineOption).HistorySavePath

Clear command history:

Remove-Item (Get-PSReadlineOption).HistorySavePath

2. Exploitation Approach

After gaining access to a Windows system, first check the PowerShell version. If it is v5, you can obtain the history by reading the file %appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

If the system uses PowerShell v3 or v4, you can install PSReadLine via the command line to record all subsequent PowerShell commands on the system

0x03 Defense Recommendations

---

If using a higher version of Windows, such as Win10, where the default PowerShell version is 5.0 and it records PowerShell commands, it is recommended to periodically clear the history. Location: %appdata%\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

Clear command history:

Remove-Item (Get-PSReadlineOption).HistorySavePath

For older versions of PowerShell, if commands contain sensitive information (such as remote connection passwords), they should be cleared promptly using the command: Clear-History

For cmd.exe, if commands contain sensitive information (such as remote connection passwords), they should be cleared promptly using the command: doskey /reinstall

0x04 Summary

---

This article introduces two types of PowerShell command history, summarizes common methods for exporting history records, combines exploitation ideas, and provides defense recommendations.