Shell history is a free flight recorder. MITRE ATT&CK T1690, Prevent Command History Logging, covers adversaries who impair it to hide the commands they run. It is in the Defense Impairment tactic and applies to ESXi, Linux, macOS, network devices and Windows.
How attackers use it
On Linux and macOS, history is tracked in a file named by the environment variable HISTFILE. When a user logs off, the content is flushed to ~/.bash_history in the home directory. HISTCONTROL decides what the history command saves, and it does not exist by default on macOS but is respected if set. Some ESXi systems also use HISTFILE.
MITRE describes the ways it is defeated:
- Clearing the variable with
unset HISTFILE, or setting the history size to zero withexport HISTFILESIZE=0. - Setting HISTCONTROL to ignorespace so commands starting with a space are not saved, or to ignoredups to skip duplicates. Some Linux systems set it to ignoreboth by default, which covers both. That means " ls" is not saved while "ls" is, and an adversary can run everything with a leading space.
On Windows, the PSReadLine module records commands from all PowerShell sessions in a file, by default $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt. The page notes adversaries can change where that history is saved with Set-PSReadLineOption.
Procedure examples on the page include a group that prepended a space to every terminal command, malware that pointed HISTFILE and MYSQL_HISTFILE at /dev/null, groups that deleted the PowerShell history file and set the save style to SaveNothing, and cases on network devices where logging or syslog was disabled.
How to detect it
MITRE gives an analytic per platform family:
- Linux. Tampering with HISTFILE, HISTCONTROL and HISTFILESIZE, and the absence of expected writes to the history file while a shell session is active.
- macOS. Suppression via the same variables, no updates to the history file, and environment variable changes tied to terminal processes.
- Windows. PowerShell history suppression with Set-PSReadLineOption using SaveNothing or an altered HistorySavePath.
- ESXi. An unset HISTFILE or modified history variables in shell sessions, and sessions that show activity but no recorded commands.
- Network devices. CLI commands that disable history logging, for example
no logging, and sessions in which commands clearly run but nothing new appears in the log.
The recurring idea is absence. A long, active session that left nothing behind is itself the signal.
This is why you should not rely on shell history alone. Shell history is written by the shell, so the user of that shell can change it. Better sources sit outside the user's reach: process execution auditing (auditd on Linux, Sysmon event ID 1 or Security event 4688 on Windows), and PowerShell script block logging (event ID 4104). Send those records to a central system.
How to reduce the risk
MITRE lists two mitigations:
- Environment variable permissions. Prevent users from changing HISTCONTROL, HISTFILE and HISTFILESIZE. For example, set them read-only in a system-wide profile script so they cannot be unset by ordinary users.
- Operating system configuration. Set HISTCONTROL to ignoredups rather than ignoreboth or ignorespace, so commands that begin with a space are still saved.
Add independent command auditing, as above. If an attacker can silence one record and not the other, you still have the evidence.
Check your own environment
Read-only checks:
# Linux or macOS, in a normal shell
echo "HISTFILE=$HISTFILE HISTCONTROL=$HISTCONTROL HISTFILESIZE=$HISTFILESIZE"
# Windows PowerShell
Get-PSReadLineOption | Select-Object HistorySaveStyle, HistorySavePath
Checklist:
- HISTCONTROL is not ignorespace or ignoreboth on shared or privileged hosts.
- Process execution auditing is on and forwarded off the host.
- PowerShell script block logging is enabled.
- Network device and ESXi shell sessions are logged centrally, not only locally.
- You have an alert for a session with activity but no history, where your tooling can express it.
Quick check
Why is a leading space a problem when HISTCONTROL is ignoreboth?
ignoreboth includes ignorespace, so any command typed with a leading space is never saved. An adversary can prepend a space to everything and leave no history.
Why should history files not be your only record of commands?
The shell user controls them and can unset, redirect or delete them. Process auditing and script block logging, sent to a central system, are harder for that user to silence.