I'm hosting a Wordpress site that got hacked. Not a big deal, usual malicious favicon_ea9b28.ico file containing PHP code.

The problem is, in this case, that after I remove the bad scripts and clean other modified files, and after I update all the plugins and themes to their latest version, the site gets hacked again. It has already happened several times since a few weeks. There must be a vulnerable piece of code that hasn't been addressed yet.

How do I find it? I mean, is there any kind of logging in PHP that logs when something creates a new file on disk? Such as script xyz.php at line 123 called fopen('favicon_ea9b28.ico', 'wb')?

EDIT: the server itself is not compromised for sure, because just removing the write permission from the directories, for the user running php-fpm, and changing the owner to root, "fixes" the problem, but then the media library becomes unusable, so that's no real fix. However it does show that the server is not compromised.

Given it's not a server compromise, this is not a duplicate, because this question assumes the server is NOT compromised. Regardless of the truth of that assumption in my particular case, I need a way to deal with a PHP web app compromise, not a server compromise.

  • 3
    Have you considered the possibility that the server itself is now hopelessly compromised and that just cleaning files may not do the trick? – ceejayoz Apr 20 '18 at 22:50
  • 2
    This is one of the many reasons that we advise blowing the server away and building a new server from scratch. You can never be sure that you have found every compromised bit and backdoor. – Michael Hampton Apr 21 '18 at 0:35
  • 3
    Re: "However it does show that the server is not compromised", that's not the case. It shows that a potential compromise doesn't have root access (or doesn't bother to use it if it does), but the user running php-fpm might still be hopelessly compromised. – ceejayoz Apr 21 '18 at 14:22
  • 5
    Possible duplicate of How do I deal with a compromised server? – Tero Kilkanen Apr 22 '18 at 1:31
  • 1
    So did you install your website from backups that are known to be working correctly? That is the only way for you to know for sure that there isn't anything malicious left hidden somewhere in the website files, which is used to reinstall the stuff on the site. Doing this rather than trying to find out what exactly is happening is much more efficient in terms of effort. There is no silver bullet in finding out possible vulnerabilities, you need to read through the source code and imagine what the possible exploitation vectors could be. That takes a lot of time. – Tero Kilkanen Apr 22 '18 at 15:36

Try installing the WordPress plugin ‘wordfence’ I use it a lot as it not only tracks live traffic you can block IPs it will also send you emails about missing security updates for plugins (including itself) and the entire WordPress installation.

It can also scan the WP files and let you know for dodgy content.

You can also manually run head and tail commands on Linux against WordPress files or grep them all, you want to look out for any base64 in the files. That part was more for the clean up, although wordfence will also identify things like this too

  • Thanks, I wasn't aware of the scan function of WordFence. It found additional modified files that I overlooked. – Lucio Crusca Apr 22 '18 at 20:09
  • Excellent! I’ve always used it and it’s never let me down! – Timothy Frew Apr 22 '18 at 20:10
  • After a few days with WordFence installed and active, new malicious files showed up anyway, so WordFence is a great tool and it helped me, but unfortunately it isn't the solution to the problem. – Lucio Crusca May 1 '18 at 6:19
  • Check your Linux auth logs. Disable php being executed in the uploads directory – Timothy Frew May 1 '18 at 6:19

It does not seem to exist any logging of the PHP function calls, but it does exist a way to hook and log any HTTP POST request the website receives, and logging includes POST data. That way you get logs even before fopen is called, so you easily find out the compromised file. The strategy is:

  1. enable POST data logging
  2. wait for the malicious files to appear
  3. look in the logs to find the compromised PHP scripts

Now each point in detail.

1) Enable POST data logging

First of all you need a PHP script that logs HTTP POST data when called. It isn't really hard to write one from scratch, but it's even easier to use an existing one that I slightly modified to make it more readable and secure:

if ( isset($_POST) && is_array($_POST) && count($_POST) > 0 ) 
  $log_dir = dirname( __FILE__ ) . '/logs/'; 
  $log_name = "posts-" . $_SERVER['REMOTE_ADDR'] . "-" . date("Y-m-d-H") . ".log"; 
  $log_entry = gmdate('r') . "\t (local time: " . date('r') . ")\t" . $_SERVER['REQUEST_URI'] . "\n";
  foreach ($_POST as $k => $v) 
    if ( $k == "pwd" || $k == "passwd" || $k == "password" || $k == "pass" || $k == "secret" )
      $v = "*******";
    $log_entry .= $k." => ".$v."\n------\n"; 
  $log_entry .= "\n///////////// END OF POST DATA /////////////\n\n\n";
  $fp=fopen( $log_dir . $log_name, 'a' );
  fputs($fp, $log_entry); 
  chmod($log_dir . $log_name, 0600);

Create two directories in your website root folder, such as postdata/logs, save the script as postdata/logger.php and set owners and permissions as appropriate for your server:

root@myserver:/home/user/htdocs/www.example.com# mkdir -p postdata/logs
root@myserver:/home/user/htdocs/www.example.com# vi postdata/logger.php 
root@myserver:/home/user/htdocs/www.example.com# chown -R user postdata
root@myserver:/home/user/htdocs/www.example.com# chmod -R a-rw postdata
root@myserver:/home/user/htdocs/www.example.com# chmod -R u+r postdata
root@myserver:/home/user/htdocs/www.example.com# chmod u+w postdata/logs

Now let's tell PHP it must run that script before anything else. You can do so by adding

php_value auto_prepend_file /home/user/htdocs/www.example.com/postdata/logger.php

to your website .htaccess file, but, since I'm running PHP-FPM which ignores .htaccess, I add that configuration to my PHP-FPM pool instead:

echo 'php_value[auto_prepend_file] = /home/user/htdocs/www.example.com/postdata/logger.php' >> /etc/php/7.1/fpm/pool.d/user.conf

and then I restart the PHP-FPM daemon.

2) Wait for the malicious files to appear

This is usually just a matter of monitoring your server. Be it by having Munin to automatically warn you when something seems wrong, or by manually using tools like find to look for recently changed files, or mailq and so on. For example

$ find . -mtime -1 -iname \*.php

will list all PHP files that have been modified in the last 24 hours. Please note that automatic updates your CMS might do are legit and they do modify PHP files, so those may count as false positives.

3) Look in the logs to find the compromised PHP scripts

cd /home/user/htdocs/www.example.com/postdata/logs
less posts-

If logged POST data look weird, that's probably a posting to the compromised script. You can find the script name in the first line of the log file:

$ head -n 1 posts-
Mon, 30 Apr 2018 22:28:40 +0000 /wp-content/plugins/one-page-navigator/admin/bsf-core/irclzkpf.php

Bingo! That was the malicious script in my case. I'm not sure there aren't any other malicious or original but compromised scripts in the website, but I just leave step 1 in place and all the rest is a piece of cake.

Please note that removing/editing that script is not enough, because by now the attacker has already used it to compromise other scripts in the site. However it's quite easy to find those by looking for base64_decode function calls or \\x.*\\x.*\\x.*\\x.*\\x.*\\x charater sequences. I think even the scan function of WordFence can find them, just in case you're running Wordpress like me.

And, while we are at it, please note that the POST data log files will contain any user credentials in plain text, except a few common cases of password fields, so my version of the logger script uses chmod to restrict access to the log files, but that falls short of any decent security measure. Take your own steps to limit access to the POST data log files and delete them right away as soon as you are finished with them.


@lucio-crusca's answer by logging POST data is very interesting.

In addition to his answer, I would like to say that when attackers infect your server they usually (not always) create a file with execution permissions.

Here is a command to find all executable files in a directory:

find /var/www/my-wordpress  -executable -type f

You can rename those files (for evaluation purposes) or just delete them.

find /var/www/my-wordpress  -executable -type f -exec mv {} /tmp/wp/{}_back \;

find /var/www/my-wordpress  -executable -type f -exec rm {} \;

I prefer to move it to /tmp because I remove the file from the web application but keep it just for if I removed the wrong file or I just want to know how is built the file.

Moreover, set the right permissions are ultra-important. I'm one of those who think that apache user (httpd, www-data, apache...) must not be able to write anything on the server but wp-content/uploads or whatever public directory you have configured. This prevents of plugins or core to be modified by attacker, if the attacker access to your server with apache user. I like to use this script to set Wordpress permissions.

Allowing www-data to write on public directories like wp-content/uploads don't prevent the attacker to write a php file and call it from outside. In this case, and using apache2 as a webserver, you can add a .htaccess file in your public directory to disable the php execution. Here is the .htaccess file that I place in wp-content/upload to avoid php execution in public directories.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.