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.shopen in new window

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:

  1. An empty, recently-modified history file would look very suspicious to anybody running forensics on the machine.
  2. 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. laurelopen in new window) 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 betteropen in new window

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:

  1. Make sure that the user's environment is not preserved whenever sudo commands are invoked.
  2. 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 with LESSSECURE=1. To accomplish this:

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 (referenceopen in new window).

SSH Connection Hiding

Credit to @Alh4zr3dopen in new window for this one (tweetopen in new window):

ssh -T $user@$target 'bash -i'

The -T option disables PTY allocation and will prevent your login from being added to /var/log/utmpopen in new window. 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.

Last Updated: