the_open_door
coffee
ESEN
← Back to index

Credential leak in PowerShell history

ATT&CKT1552.001

PowerShell, through the PSReadLine module, keeps on disk a history of every command the user has typed in interactive sessions. That file persists across reboots and, importantly for us, is readable by the user themselves with no special privileges. The path is confirmed and read like this:

(Get-PSReadLineOption).HistorySavePath
gc (Get-PSReadLineOption).HistorySavePath
type $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt

(equivalent to C:\Users\<user>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt).

It is one of the first places worth checking after getting a shell on Windows, because people type things into the console they should not: passwords passed as arguments, connection strings, ConvertTo-SecureString '...' -AsPlainText -Force commands, calls to Invoke-Command or evil-winrm with the credential in cleartext, etc. A single history line can contain another user's password and open a lateral pivot.

Search across profiles and by keyword

With privileges to read other profiles, you sweep all histories and filter for juicy terms:

Get-ChildItem C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt -Force |
  ForEach-Object { Select-String -Path $_ -Pattern 'password|secret|ConvertTo-SecureString|-p ' }

The classic pattern is finding a block like:

$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)
Invoke-Command -ComputerName localhost -Credential $c ...

From there you read svc_deploy's password directly and reuse it to authenticate as that user. It is worth checking other cleartext stores while you are at it: cmdkey /list lists saved credentials (reusable with runas /savecred), and web.config, unattend.xml or registry autologon (Winlogon) hold the same kind of secrets. It is not a software vulnerability but bad operational practice: leaving secrets in cleartext on the command line.

Requirements

A shell on the host and read permission on the history file (your own, always; other users', only with privileges). The value depends entirely on someone having typed a secret into the console.

How to detect it. Reading the file leaves little trace on its own, but there are two angles. The first, and most powerful, is the source: if PowerShell Script Block Logging is enabled (Event ID 4104), the very command that left the credential in the history was logged at the time, so the defender can hunt in the 4104 events for ConvertTo-SecureString ... -AsPlainText with cleartext secrets before an attacker finds them. The second is access: reading ConsoleHost_history.txt appears as a 4688 (type/Get-Content over that path) or, with a SACL on the file, a 4663. Later reuse of the leaked credential (a 4624 for svc_deploy from an unusual host) closes the chain.

Defence. Do not enter credentials as cleartext in the console (use interactive Get-Credential, secret managers or managed accounts), and clear or disable PSReadLine history on sensitive accounts (Set-PSReadLineOption -HistorySaveStyle SaveNothing). It is a specific form of credential hunting and is usually chained with reading laps when the leaked user belongs to a group with interesting permissions.

ToolsSeatbelt

Machines where it's used