Linux Evasion Techniques
How to evade detection on Linux.
Amnesiac Shell (bash)
TIP
Also check out my script that performs everything described in this section: amnesia.sh
There are a few uncommon environment variables and shell options that you can tamper with in order to avoid shell history-based detections.
First, there's a shell option for enabling/disabling history. Let's go ahead and disable that option:
set +o history
The HISTFILE
environment variable controls where your command history is stored. Let's not:
unset HISTFILE
The HISTSIZE
environment variable controls of the number of commands to remember in the command history. Let's prevent commands from appearing in any sort of in-memory buffer:
export HISTSIZE=0
TIP
I don't recommend using unset HISTSIZE
because if you do, your commands won't end up in the in-memory buffer, but you'll still be able to access all prior shell history. This is massively annoying if you have the muscle memory to press the up/down arrow keys.
The HISTFILESIZE
environment variable controls the number of lines that will be stored in the history file. If you'd like to be 100% sure that all history is erased after the shell exits, you can also run:
export HISTFILESIZE=0
However, this should be used with caution because:
- An empty, recently-modified history file would look very suspicious to anybody running forensics on the machine.
- It may be annoying if you end revisiting the box because there could've been loot in the shell history, but you've already deleted it. 😒
Putting it All Together
For a basic amneisac shell, use vi
(or any other available editor) to create an innocuous-looking script. For example, this is what I like to use:
vi "${HOME}/.bash_profile.bak"
Add this to the script, then save and exit:
#!/bin/bash
{
unset HISTFILE
export HISTSIZE=0
set +o history
# Optionally:
rm -f "${HOME}/.bash_profile.bak"
}
The reason for creating a script is, well, history-based detections may spot your evasion attempts if you run those commands directly in the terminal. Unless there's some process inspecting all scripts on the system (highly unlikely due to the performance hit), nobody's gonna notice a random script. Especially if the script in question is a "backup" of .bash_profile
.
Now, if you were thinking you should just run this script, nope! You should source it instead. In this case, that'd be:
source "${HOME}/.bash_profile.bak"
By sourcing the script, it'll be as if you've run each command one-by-one in your shell, so the environment changes will apply to your shell session.
Additionally, notice that the commands are inside curly brackets. By doing this, all of the commands will be loaded into memory at once before running. This allows us to delete the script as it's being run, and as a bonus, the source
command won't even be saved to the history file!
WARNING
Don't use rm -f "${0}"
in your script if you adapt this setup! You may think that it points to the script path, but when you source a script, that's actually the path to bash
itself.
Extras
If you want to be extra thorough, there are even more environment variables you can mess with.
If you've SSH'd in to the machine:
unset SSH_CLIENT
unset SSH_CONNECTION
unset SSH_TTY
WARNING
The SSH environment variables won't help much unless somebody's inspecting a process live. Logs are a thing.
If you've escalated privileges with sudo:
unset LOGNAME
unset SUDO_USER
WARNING
Again, logs are a thing.
To avoid logging commands when running them inside less
(useful for journalctl
sudo abuse, particularly if the user environment is preserved):
export LESSHISTFILE=/dev/null
export LESSHISTSIZE=0
export LESSECURE=0
TIP
Wait, less
can run shell commands? Yup! This feature exists for the convenience of sysadmins, e.g. you need to run a command while viewing X output, but don't want to ditch the output yet.
For Python:
echo "import readline" > /tmp/.pythonrc.py
echo "readline.set_history_length(0)" >> /tmp/.pythonrc.py
export PYTHONSTARTUP=/tmp/.pythonrc.py
export PYTHONDONTWRITEBYTECODE="True" # not 100% necessary, but it doesn't hurt
For MySQL:
export MYSQL_HISTFILE=/dev/null
If you need to pivot to other hosts using SSH, you can make sure you don't disturb the known_hosts
file with this alias:
alias ssh='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
Defensive Measures
First and foremost, if you plan to or currently rely on shell history for detecting malicious activity, don't. As you can see, it's fairly trivial to evade.
There may be specific mitigations depending on what you're aiming to detect, but a comprehensive monitoring solution like auditd
(along with a tool to make the output easier to ingest/parse, e.g. laurel) is generally a good idea because it will grant you far more detailed and reliable telemetry for spotting malicious behavior. Of course, ingesting auditd
logs is definitely a challenge at scale, but I'd say it's worth serious consideration in order to avoid blind spots and build more robust detections.
Sudo Abuse
When it comes to sudo
abuse, there should be a dedicated log file containing all commands run with sudo
. The sudo
log file is a better source than shell history because the sudo
binary controls this file, not the user, so users cannot tamper with the logging unless they've already rooted the system. In other words, if sudo
is properly configured, attackers cannot evade detection.
The location of the sudo
log file depends on distro and of course your own setup. The default is /var/log/secure
on RHEL-based systems, and /var/log/auth.log
on Debian-based systems. Alternatively, sudo
can also log to syslog
if desired.
TIP
To learn more about configuring logging for sudo
, I discovered this blog post while I was fact-checking the remediation: Sudo for blue teams: how to control and log better
The blog post does mention features present in newer versions of sudo
, though, so beware if you're not using (and/or willing to support) the latest-and-greatest versions.
less
/journalctl
Abuse
Along those lines, there are specific recommendations for less
/journalctl
abuse in particular:
- Make sure that the user's environment is not preserved whenever
sudo
commands are invoked. - Disable shell commands inside of
less
. There are a couple of options to accomplish this:- Recompile
less
with the "secure" mode. This is more of a headache, but it's a more robust fix. - Ensure that
less
is always invoked withLESSSECURE=1
. To accomplish this:- Enable the
pam_env
PAM module. - Create the
/etc/environments
file and addexport LESSSECURE=1
to it.
- Enable the
- Recompile
If you're concerned about people escaping a restricted shell, you can also export LESSSECURE
as a read-only environment variable via /etc/profile
(or similar). Just beware that in some conditions, it's possible to unset read-only environment variables (reference).
SSH Connection Hiding
Credit to @Alh4zr3d for this one (tweet):
ssh -T $user@$target 'bash -i'
The -T
option disables PTY allocation and will prevent your login from being added to /var/log/utmp
. As a result, you won't appear if somebody runs w
or who
.
I personally don't use this very frequently because it's more useful if you expect somebody to actually be checking the output and/or performing forensics with utmp
, and the shell is more limited than you may expect. That being said, it's still worth keeping around for whenever that's a concern.