We all know why bad actors infect sites: monetary gain, boosts in SEO ratings for their malware or spam campaigns and a number of other reasons explained in our post on hacker’s motivations.
It defeats the purpose of the attack if the malware is easily and quickly removed. Attackers have developed some methods for protecting their work as we will explore in this post. We will also look at how you can remove this infection from a compromised website.
What does this malware look like?
In most cases of this type of infection, we will find a modified index.php:
Example of an infected index.php file that automatically re-generates itself through a malicious process running in the background
It doesn’t matter if your site is not running WordPress, the attackers will usually replace the index.php with an infected copy of the WordPress index.php file.
We often also see hundreds or sometimes thousands of infected .htaccess files scattered throughout the website directories. This is designed to prevent custom PHP files or tools from running on the site or to allow the malicious files from running in case there’s some mitigation already in place.
Example of an infected .htaccess file which interferes with the ability to run most PHP scripts
In rare circumstances, the attackers will leave a copy of the original index.php file on the server named old-index.php or 1index.php that we can rename back to index.php. In most cases, the infected files will have been changed to 444 permissions and attempting to remove or clean those files directly is unsuccessful since the malware will immediately create a new infected copy.
Cleaning the infection
First steps
As we saw from the infected .htaccess, the attackers have created a list of files allowed to run on the server: about.php, radio.php, etc, preventing any other PHP files from loading. These files will usually not exist on the server but will run as malicious processes. The persistent, running processes on the server are what allows the malware to automatically and immediately reinfect the site once the infection is removed.
The first step to attempt to stop the malware is to create a file from one of those names and add the following content. For example, in radio.php
<?php echo shell_exec("ps aux | grep -i php | awk {'print $2'} | xargs kill -9") ?>
Note: The file name must match one of those listed in .htaccess as those are the only files that are allowed to run on the server. This will ensure our command is successfully run.
The contents of this file will look for all running PHP processes and attempt to stop those.
We can then load that file in the browser.
hxxps://yourwebsite[.]com/radio.php
You won’t see any content in the browser but if the process was successful, you should be able to rename or delete .htaccess and index.php without seeing a new infected copy being created. If you are not able to access the file you created, you will need to proceed with the SSH steps below.
It is important to note that some malware may not re-create the infected files immediately, and you will want to load your site a couple of times, checking for the reinfected files after each attempt.
Once you have confirmed that the files are not going to return, you will need to remove the remainder of the infection.
Persistence via WordPress core files
If the malware is still present, it is possible that the re-infector exists somewhere in the core WordPress files. One method we frequently see is a modified wp-includes/plugin.php file designed to re-create the index.php and .htaccess:
Reinfector malware placed within the WordPress core files
After removing that content, the index.php and .htaccess should be unlocked and you can proceed with cleaning those files along with the remainder of the infection. Though plugin.php is a common point of attack, we have seen similar code on other core files.
One option you have is to replace all of the core site files with fresh copies, and to reinstall your themes and plugins. Some reinfectors are heavily obfuscated and are designed to remain well hidden. It is also not uncommon for the attackers to upload fake plugins to the wp-content/plugins directory that will not be visible from wp-admin.
Proceeding via ssh
If previous attempts to clean the infected index.php or .htaccess have been unsuccessful, you may need to gain SSH access or load a CPanel terminal to check running processes.
Run the top command (and press the ‘c’ key to expand the output) or “ps -aux” and look for anything strange there. Often these cases will reveal something like this:
wp-content/uploads/2021/lock360.php
Or this:
wp-includes/l.php
A malicious process running on the server designed to immediately reinfect the website once files are modified. This can be seen by running “ps -aux” or “top”
In this case, we can see the process running with PID 664739 and we can kill that process.
The “kill” command attempts to terminate the specified process and the -9 flag will attempt to “force quit” it.
If the offending process was responsible for recreating index.php, you should be able to rename the file without seeing a new copy dropping in, and you should be able to proceed with cleaning the remainder of the infection.
Dealing with memory-based malware
In rare cases, the malware will reside in php-fpm memory. If index.php is still being re-created after the above steps have been completed, run top and check for the presence of php-fpm.
PHP-FPM processes running on a server
Though this usually will not correct the problem, you can attempt to clear OPCache. Create a file in the site’s document root named opcache.php:
<?php if(function_exists("opcache_get_status") && is_array(opcache_get_status())) { if (opcache_reset()) { echo "OPCache has been cleared."; } else { echo "OPCache could not be cleared."; } } else { echo "OPCache is not available"; }
OPcache improves PHP performance by storing pre-compiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request. Because of this, malware can persist in OPcache after being cleaned from the site files or database.
You can then test that in the browser and this should attempt to flush the OPcache:
hxxp://yourwebsite[.]com/opcache.php
If OPcache is not enabled, or clearing that did not fix the issue, php-fpm will need to be restarted. You may need sudo access to re-start the service. However, if there are multiple sites on the server then they will all need to be cleaned, otherwise they will likely reinfect each other.
Please note that restarting the service will break all active sessions in all sites – there isn’t any way to target a specific php-fpm pool for these purposes. Restarting php-fpm will also depend on the Linux distribution in use and the specific name/version of the service.
If the malware is still there, we will need to investigate further.
Conclusion
Though attackers are always looking for new ways to infect sites, there are some common steps you can take to minimize those infections.
- Put your website behind a Firewall.
- Regularly change all admin passwords associated with your site. This includes the admin dashboard, CPanel/FTP, ssh and email. Read our blog post on the process of creating secure passwords.
- Keep all plugins, themes and your CMS up to date at all times; remove any unneeded plugins or themes – attackers are always on top of new and undiscovered vulnerabilities.
If you have not subscribed to our Website Security services, we offer options to clean the malware and keep your site protected.