How to scan for malware via SSH?

by May 1, 2023Malware, WordPress Security0 comments

Kinsta - Unlock 4 Months OFF Annual WordPress Plans

Detecting malware on a WordPress site from the command prompt can be a useful method for webmasters and security professionals who prefer command-line interfaces. In this blog post, we will discuss different terminal commands that can be used to detect malware on a WordPress site.

Table of Contents

Requirements

To make the most of the following article, we expect that you have the following:

  • A web hosting account or server with SSH access.
  • A SSH client already installed in your computer.
  • You have basic PHP knowledge.

In this tutorial we will be using the following services & tools:

Kinsta – Premium WordPress Hosting

Kinsta as our Premium Managed hosting provider for WordPress.

Some features we love from Kinsta:

  • Full SSH support.
  • GIT included.
  • WP-CLI
  • Cron Jobs
  • Latest PHP versions.
  • Free Malware removal service.
iTerm2 for MacOS
iTerm2 for MacOS

iTerm2 for MacOS as our SSH client. iTerm2 is a replacement for MacOS built-in Terminal app. We use due to these amazing features:

  • Split pane view. If you usually have several SSH sessions this helps a lot.
  • Search – you can search within your session and increase the session to record longer times.

Before you start, make a backup

As we’ve talked about before, Backups are and should be a huge part of your website strategy.

But as a quick reminder, here is a few bullet points:

  • Backups should be saved in a different server for disaster recovery purposes. If the server website goes down, you need to have a access to a backup to restore your site on a different server. If you save the backups & website on the same website, you will be out of luck.
  • Backups should be scheduled at least daily depending on your business needs. Some eCommerce websites do hourly backups, so please check which frequency makes sense and fit within your budget.
  • Backups should include both the filesystem and the database contents. WordPress websites save information in both files and the database so to successfully restore a site you will need both.

Step 1: Connect to the Server

First, you need to connect to the server hosting the WordPress site using an SSH client. You will need to provide your credentials to log in. Once logged in, navigate to the WordPress site directory.

On this tutorial, we will assume you already know:

  • How and were to get the SSH credentials or SSH Keys
  • Server SSH connection information
  • How to successfully connect to a server via SSH.
Logged in to my test website @ Kinsta.com [SSH details and OS info obscured for security]

Step 2: Check for Suspicious Files

You can use the following commands to check for suspicious files on your WordPress site:

  1. List all files and directories in the current directory:
ls -la

This command will list all files and directories in the current directory, including hidden files.

Please be aware that WordPress by default has 3 different folders: wp-admin, wp-content & wp-includes. This means that any other folder contained there might be suspicious if you did not create it.

Below an example on how a clean installation should look like:

$ ls -la
total 331
drwxrwxr-x  5 wpmechanicsblog wpmechanicsblog    21 Apr 28 21:34 .
drwxr-xr-x  8 wpmechanicsblog wpmechanicsblog    30 Apr 28 21:33 ..
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   405 Apr 28 21:34 index.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 19915 Apr 28 21:34 license.txt
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7402 Apr 28 21:34 readme.html
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7205 Apr 28 21:34 wp-activate.php
drwxrwxr-x  9 wpmechanicsblog wpmechanicsblog   101 Apr 28 21:34 wp-admin
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   351 Apr 28 21:34 wp-blog-header.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2338 Apr 28 21:34 wp-comments-post.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3013 Apr 28 21:34 wp-config-sample.php
drwxrwxr-x  4 wpmechanicsblog wpmechanicsblog     5 Apr 28 21:34 wp-content
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  5536 Apr 28 21:34 wp-cron.php
drwxrwxr-x 28 wpmechanicsblog wpmechanicsblog   251 Apr 28 21:34 wp-includes
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2502 Apr 28 21:34 wp-links-opml.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3792 Apr 28 21:34 wp-load.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 49330 Apr 28 21:34 wp-login.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  8541 Apr 28 21:34 wp-mail.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 24993 Apr 28 21:34 wp-settings.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 34350 Apr 28 21:34 wp-signup.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  4889 Apr 28 21:34 wp-trackback.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3238 Apr 28 21:34 xmlrpc.php

Sometimes malware is easy to spot on this step, here are some simple Indicators of Compromise to have in mind:

  • The main file index.php size should be between 405 and 420 bytes. Any other size on this file, means that the file was intentionally modified. So be sure to check it contents with your favorite text editor such as vim, nano or using other terminal commands such as cat, less or more.
  • Folders with random names is another good indication that you have malware. See the example below that contains 3 directories with random names 7kafDso, af6jdh & Dog7ndw:
$ ls -la
total 332
drwxrwxr-x  8 wpmechanicsblog wpmechanicsblog    24 Apr 28 21:40 .
drwxr-xr-x  8 wpmechanicsblog wpmechanicsblog    30 Apr 28 21:33 ..
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 7kafDso
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 af6jdh
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 Dog7ndw
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   405 Apr 28 21:34 index.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 19915 Apr 28 21:34 license.txt
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7402 Apr 28 21:34 readme.html
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7205 Apr 28 21:34 wp-activate.php
drwxrwxr-x  9 wpmechanicsblog wpmechanicsblog   101 Apr 28 21:34 wp-admin
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   351 Apr 28 21:34 wp-blog-header.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2338 Apr 28 21:34 wp-comments-post.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3013 Apr 28 21:34 wp-config-sample.php
drwxrwxr-x  4 wpmechanicsblog wpmechanicsblog     5 Apr 28 21:34 wp-content
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  5536 Apr 28 21:34 wp-cron.php
drwxrwxr-x 28 wpmechanicsblog wpmechanicsblog   251 Apr 28 21:34 wp-includes
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2502 Apr 28 21:34 wp-links-opml.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3792 Apr 28 21:34 wp-load.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 49330 Apr 28 21:34 wp-login.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  8541 Apr 28 21:34 wp-mail.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 24993 Apr 28 21:34 wp-settings.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 34350 Apr 28 21:34 wp-signup.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  4889 Apr 28 21:34 wp-trackback.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3238 Apr 28 21:34 xmlrpc.php
  • There can also be filenames with random names that could be malware, be sure that all WordPress core files that sit on the public directory start with wp-, if you have files with other naming convention, then they should be easy to spot just by doing the directory listing. In the example below there is a file called options.php. This file is not part of WordPress because it does not start with wp- and it’s not WordPress main index.php file.
$ ls -la
total 337
drwxrwxr-x  8 wpmechanicsblog wpmechanicsblog    25 Apr 28 21:45 .
drwxr-xr-x  8 wpmechanicsblog wpmechanicsblog    30 Apr 28 21:33 ..
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 7kafDso
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 af6jdh
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 Dog7ndw
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   405 Apr 28 21:34 index.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 19915 Apr 28 21:34 license.txt
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  1102 Apr 28 21:45 options.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7402 Apr 28 21:34 readme.html
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7205 Apr 28 21:34 wp-activate.php
drwxrwxr-x  9 wpmechanicsblog wpmechanicsblog   101 Apr 28 21:34 wp-admin
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   351 Apr 28 21:34 wp-blog-header.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2338 Apr 28 21:34 wp-comments-post.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3013 Apr 28 21:34 wp-config-sample.php
drwxrwxr-x  4 wpmechanicsblog wpmechanicsblog     5 Apr 28 21:34 wp-content
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  5536 Apr 28 21:34 wp-cron.php
drwxrwxr-x 28 wpmechanicsblog wpmechanicsblog   251 Apr 28 21:34 wp-includes
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2502 Apr 28 21:34 wp-links-opml.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3792 Apr 28 21:34 wp-load.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 49330 Apr 28 21:34 wp-login.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  8541 Apr 28 21:34 wp-mail.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 24993 Apr 28 21:34 wp-settings.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 34350 Apr 28 21:34 wp-signup.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  4889 Apr 28 21:34 wp-trackback.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3238 Apr 28 21:34 xmlrpc.php
  1. List all files and directories in the current directory sorted by modification time:
ls -lat

This command will list all files and directories in the current directory, sorted by modification time, with the most recently modified files listed first.

See the example below:

$ ls -lat
total 337
drwxrwxr-x  8 wpmechanicsblog wpmechanicsblog    25 Apr 28 22:13 .
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  4024 Apr 28 22:13 wp-load.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  1102 Apr 28 21:45 options.php
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 7kafDso
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 Dog7ndw
drwxrwxr-x  2 wpmechanicsblog wpmechanicsblog     2 Apr 28 21:40 af6jdh
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7402 Apr 28 21:34 readme.html
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  7205 Apr 28 21:34 wp-activate.php
drwxrwxr-x  9 wpmechanicsblog wpmechanicsblog   101 Apr 28 21:34 wp-admin
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  5536 Apr 28 21:34 wp-cron.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 34350 Apr 28 21:34 wp-signup.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   405 Apr 28 21:34 index.php
drwxrwxr-x 28 wpmechanicsblog wpmechanicsblog   251 Apr 28 21:34 wp-includes
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2502 Apr 28 21:34 wp-links-opml.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 24993 Apr 28 21:34 wp-settings.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 19915 Apr 28 21:34 license.txt
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog   351 Apr 28 21:34 wp-blog-header.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog 49330 Apr 28 21:34 wp-login.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  8541 Apr 28 21:34 wp-mail.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  4889 Apr 28 21:34 wp-trackback.php
drwxrwxr-x  4 wpmechanicsblog wpmechanicsblog     5 Apr 28 21:34 wp-content
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  2338 Apr 28 21:34 wp-comments-post.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3013 Apr 28 21:34 wp-config-sample.php
-rw-rw-r--  1 wpmechanicsblog wpmechanicsblog  3238 Apr 28 21:34 xmlrpc.php
drwxr-xr-x  8 wpmechanicsblog wpmechanicsblog    30 Apr 28 21:33 ..

On this example we can see that on top we have the wp-load.php, which is a core WordPress file and having such file recently modified is never good news.

Below are some Indicators of Compromise to consider when you’re reviewing timestamps:

  • All core WordPress files should have the same time-stamp. If any has a different one, it means it was manually modified and it will require our review. The only file that its usual to have a different time-stamp is wp-config.php, this is because this file is edited after WordPress was installed since the last step is to add the MySQL database credentials.
  • Review all the most recent edited files.
  • Depending on your server or hosting provider, you can use the command stat to gather further information about the file. in the example below you can see further information about changes along with its timestamps.
# stat wp-load.php
  File: wp-load.php
  Size: 4024      	Blocks: 9          IO Block: 4096   regular file
Device: 100078h/1048696d	Inode: 547415      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1001/wpmechanicsblog)   Gid: ( 1003/wpmechanicsblog)
Access: 2023-04-28 22:13:15.047832117 +0000
Modify: 2023-04-28 21:18:40.953886652 +0000
Change: 2023-04-28 21:18:40.953886652 +0000
 Birth: -
  1. List all PHP files in the current directory:
find . -name "*.php"

This command will list all PHP files in the current directory and its subdirectories.

  1. Check for files that have been modified in the last 24 hours:
find . -type f -mtime -1 -ls

This command will list all files in the current directory and its subdirectories that have been modified in the last 24 hours.

  1. Check for files that have been modified in the last 24 hours and have the .php extension:
find . -type f -name "*.php" -mtime -1 -ls

This command will list all PHP files in the current directory and its subdirectories that have been modified in the last 24 hours.

Step 3: Check for Suspicious Code

Once you have identified suspicious files, you can use the following commands to check for suspicious code:

  1. Search for a specific string of code in a file:
grep -r "string" /path/to/directory

This command will search for the specified string of code in all files in the specified directory and its subdirectories.

  1. Search for a specific string of code in all PHP files in a directory:
grep -r "string" --include="*.php" /path/to/directory

This command will search for the specified string of code in all PHP files in the specified directory and its subdirectories.

  1. Search for a specific string of code in all PHP files in a directory and output the results to a file:
grep -r "string" --include="*.php" /path/to/directory > output.txt

This command will search for the specified string of code in all PHP files in the specified directory and its subdirectories and output the results to a file named output.txt.

Dangerous PHP functions

Some of the suggested search strings are the following PHP functions which are often used by malicious actors:

  • base64_decode – Decodes the given base64 formatted string.
  • eval – Evaluate a string as PHP code.
  • assert – Identical to eval()
  • exec – Returns last line of the given command’s output
  • shell_exec – Returns the given command’s output
  • system – Passes commands output directly to the browser and returns last line.
  • gzinflate – Applies gzip decompression to given string.
  • str_replace – Replaces the given string in the specified code block, array, or file.
  • preg_replace – Same as str_replace but with RegEX support.
  • passthru – Passes commands output directly to the browser.
  • str_rot13 – Encodes the given string or content in ROT13 format.
  • htmlspecialchars_decode
  • gzuncompress

zxczxc

Step 4: Checking for Malware & SEO SPAM in your database

Since WordPress also uses a MySQL database to store settings and website content; malicious actors often place JS redirectors, PHP Code or malicious SEO SPAM blocks of code into the database; so here will provide a few example commands to help you find them.

If you’ve read any of our previous content, you know we LOVE WP-CLI and all its features. But if you have not, pause here and take a quick peek at these articles:

Now, let’s get into the meaty stuff. For checking our database we will use WP-CLI’s command wp db search. This will allow us to easily scan the database without having to login to MySQL CLI or using a 3rd party desktop app such as MySQL WorkBench, Navicat or Toad.

To have a better understanding of what it makes sense to look for, let’s review some of the most used functions and keywords used in malware (and SEO SPAM) that we can find in WordPress databases:

Dangerous Javascript functions

  • eval – just as with PHP, evaluates a string as Javascript code.
  • atob – decodes a string of data which has been encoded using base64 encoding.
  • btoa – opposite to atob, it encodes a string using base64 encoding.
  • String.fromCharCode – returns a string created from the specified sequence of UTF-16 code units.

SEO SPAM Keywords

  • Viagra
  • Cialis
  • Levitra
  • Sildenafil
  • Xanax
  • Nexium
  • Allegra
  • Lipitor
  • Oxycodone
  • Omeprazole
  • Amoxil
  • Ativan
  • Ambien
  • Claritin
  • Lexapro
  • Nasonex
  • Prozac
  • Ritalin
  • Rogaine
  • Vicodin
  • Adult-themed SEO Spam. This includes the links to adult toys stores, adult content websites and similar.
  • Sports wear and shoes Spam. Started offering NBA and NFL replica jerseys and then also started included selling replicas of Nike, Jordan, Adidas and other brands.

Let’s now show some examples of commands to use:

wp db search fromCharCode --all-tables

This command will search for the fromCharCode function in all the tables of the WordPress database due to the --all-tables parameter. This will match malicious obfuscated JS code like the example below:

eval(String.fromCharCode(118, 97, 114, 32, 95, 112, 97, 113, 32, 61, 32, 95, 112, 97, 113, 32, 124, 124, 32, 91, 93, 59, 10, 32, 32, 95, 112, 97, 113, 46, 112, 117, 115, 104, 40, 91, 39, 116, 114, 97, 99, 107, 80, 97, 103, 101, 86, 105, 101, 119, 39, 93, 41, 59, 10, 32, 32, 95, 112, 97, 113, 46, 112, 117, 115, 104, 40, 91, 39, 101, 110, 97, 98, 108, 101, 76, 105, 110, 107, 84, 114, 97, 99, 107, 105, 110, 103, 39, 93, 41, 59, 10, 32, 32, 32, 32, 118, 97, 114, 32, 117, 61, 34, 104, 116, 116, 112, 115, 58, 47, 47, 118, 111, 105, 112, 110, 101, 119, 115, 119, 105, 114, 101, 46, 105, 110, 110, 111, 99, 114, 97, 102, 116, 46, 99, 108, 111, 117, 100, 47, 34, 59, 10, 32, 32, 32, 32, 95, 112, 97, 113, 46, 112, 117, 115, 104, 40, 91, 39, 115, 101, 116, 84, 114, 97, 99, 107, 101, 114, 85, 114, 108, 39, 44, 32, 117, 43, 39, 112, 105, 119, 105, 107, 46, 112, 104, 112, 39, 93, 41, 59, 10, 32, 32, 32, 32, 95, 112, 97, 113, 46, 112, 117, 115, 104, 40, 91, 39, 115, 101, 116, 83, 105, 116, 101, 73, 100,  111, 100, 101, 46, 105, 110, 115, 101, 114, 116, 66, 101, 102, 111, 114, 101, 40, 103, 44, 115, 41, 59));

Conclusion

Using the command prompt to detect malware on a WordPress site can be an effective method for webmasters and security professionals. By using the above-mentioned commands, you can quickly and easily identify suspicious files and code on your site, which can help you take action to remove any malware and protect your website from further attacks.

Kinsta - Unlock 4 Months OFF Annual WordPress Plans
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
WPScan Cheat Sheet

WPScan Cheat Sheet

WPScan is an invaluable tool for safeguarding your WordPress website against potential vulnerabilities. As cyber threats continue to evolve, performing regular scans with WPScan can help identify security weaknesses and protect your website from potential attacks.

In this blog post, we’ll provide you with a comprehensive WPScan cheat sheet that covers installation, basic scanning techniques, password brute-forcing, vulnerability scanning, plugin and theme analysis, output and reporting options, and more. Let’s dive in and unlock the power of WPScan to fortify your WordPress fortress.

read more
4 Free Tools To Scan WordPress For Security Vulnerabilities

4 Free Tools To Scan WordPress For Security Vulnerabilities

As the popularity of WordPress continues to grow, so does the need for robust security measures to protect your website from potential vulnerabilities. Fortunately, there are free tools available that can scan your WordPress site and identify security weaknesses.

In this blog post, we will explore four powerful tools that can help you fortify your WordPress fortress. Each tool is accompanied by an explanation, link, and screenshots, providing you with a comprehensive overview of their features and capabilities.

read more
What is the best WordPress security?

What is the best WordPress security?

In today’s digital landscape, protecting your WordPress website from potential threats is crucial. With cyberattacks on the rise, implementing robust security measures is paramount.

This blog post delves into the world of WordPress security, exploring the best practices and tools to fortify your online presence. Discover how you can keep your website secure and gain peace of mind in an increasingly interconnected world.

read more
Understanding How Passwords are Stored in WordPress

Understanding How Passwords are Stored in WordPress

Passwords serve as the first line of defense against unauthorized access to your website. As one of the most popular content management systems (CMS) in the world, WordPress takes the security of user passwords seriously.

In this article, we will delve into the inner workings of password storage in WordPress, exploring the mechanisms implemented to ensure the protection of user credentials.

read more
WordPress Password Manager SSO (Single Sign-On): Simplify Access, Enhance Security

WordPress Password Manager SSO (Single Sign-On): Simplify Access, Enhance Security

In today’s digital landscape, managing multiple usernames and passwords across various platforms can be a daunting task. That’s where Single Sign-On (SSO) comes in.

In this comprehensive blog article, we will delve into the world of WordPress Password Manager SSO, exploring its history, benefits, top plugins to implement SSO in a WordPress site, common implementation errors, and the importance of SSO in building a robust WordPress authentication strategy.

read more