Linux Can Get Viruses Too (Apparently) or: How I Learned To Clean An Infected WordPress Install

I’ve been having a hell of a time combating my web server that this site runs on. It was first hit by some DDOSing, then someone exploited Revolution Slider which I had on another domain hosted on the same box. I ended up going through line by line in my themes and custom plugins looking for anything that could mess with the site.

In WordPress, most exploits usually end up being one of two things. The first is rogue PHP files in the uploads directory. The web server user (www-data in my case) has read and write permissions to this directory. This is easy enough to scan for:

cd /path/to/wordpress/wp-content/uploads
sudo find . -name "*.php"

This will spit out a list of PHP files in your uploads directory. Simple. You should probably remove all of these unless you know what they are.

Other exploits are a bit more nefarious. They will actually edit your core, theme, and plugin files and inject their own code. They use two functions in PHP to mask what they are doing, base64_decode() (or imap_base64()) and eval(). These are fairly normal functions. base64_decode() does just what its name implied, it decodes a base64 encoded string. A base64 encoded string is nothing special. It is not encrypted nor is it malicious. “Hackers” like to use base64 for obfuscating their code. eval() is also a fairly normal function. It takes a string and executes it as PHP.

If you team these up together you get something interesting. For example:

$hello_world = 'hello world';
$hello_world_64 = base64_encode('hello world');

echo $hello_world; // Prints 'hello world'
echo $hello_world_64; // Prints 'aGVsbG8gd29ybGQ='
echo base64_decode($hello_world_64); // Prints 'hello world'

$hello_world = 'echo "hello world"';
eval($hello_world); // Prints 'hello world'

$hello_world_command = base64_encode("echo 'hello world'");
echo $hello_world_commmand; // Prints 'ZWNobyAnaGVsbG8gd29ybGQnOw=='
eval(base64_decode($hello_world_command)); // Prints 'hello_world'

So, you can see how thisĀ could be used to mask your code. Again, there’s valid scenarios to use these functions, but I’ve seen them most commonly used by people trying to exploit a PHP-based site.

We can scan for base64 functions pretty easily:

cd /path/to/wordpress
grep -r base64_decode *

Keep in mind that not every results will mean that file is exploited. An easy way to cut down on the number of results is to delete all plugins or themes that you can download from their original source. You should also delete the core WordPress files. Actually, you should absolutely do this if you’re site has been exploited. To reiterate: delete WordPress’s core files and delete all plugins and themes that can be downloaded from their original source. You should only be scanning custom themes / plugins.

One last thing I did not think of to do was to see if anything else on the system was exploited. I’m sure there’s a million ways to check this. The one thing I didn’t check after I cleaned everything was my temporary directories, mainly /tmp and /var/tmp.

sudo rm -R /tmp/*
sudo rm -R /var/tmp/*

After that you’ll want to check your startup scripts. Make sure nothing is starting that you don’t want to. Exploiters usually aren’t very clever and some scripts will be named with what seems like a random string of characters.

Finally, and this is what solved my issue, you’ll want to scan your cron tasks.

su -
for user in $(cut -f1 -d: /etc/passwd); do echo $user; crontab -u $user -l; done

The su – is used to gain root access. You will need to be root to view everyone’s cron tasks. When I ran this on my server I noticed that www-data had a cron job scheduled that looked odd

www-data
*/15 * * * * /var/tmp/QzKVVrgvX >/dev/null 2>&1

Sure enough, in the tmp directory there was an ELF binary. I grabbed a md5 hash of the file, Googled it, and sure enough it was a known virus. Easy enough, I just removed the cron job.

Finally after all this was done I did the most important step of all:

sudo apt-get upgrade

I always make sure I’m on the latest version of WordPress. My server’s OS deserves the same treatment.

Okay, there’s one last step.

Great! You’ve read this far! This next sentence is by far the most important advice I have:

Ignore all of the above. Spin up a new server, configure it, and restore from backup. After following all of the above, I was not able to remove everything and I still had issues. My solution was to spin up a new VPS and start fresh.

Leave a Reply

Your email address will not be published. Required fields are marked *