Hacked (pt.3) or how to clean a compromised WordPress site

The word 'hacked' within ones and zeros.
Source: iStock (10623991)

This is the kind of post that I’ve thankfully not needed to post for over nine years. Today one of my WordPress sites got compromised.

It all began with an email this afternoon from AntiVirus a WordPress plugin that scans your theme templates for malicious code injections. The email read:

The daily antivirus scan of your blog suggests alarm.

I had to laugh at the phrase “suggests alarm”. But after I laughed, I accepted their suggestion and for a few moments felt alarm, before realising that panic was no use and besides, I knew what to do.

Two candidates

I’m still not 100% sure what caused the code injection but I currently suspect two potential sources of infection:

MailPoet

I may have been one of 50,000+ victims of the MailPoet vulnerability that was made public days before I went down with viral meningitis! I had that

As the MailPoet site states:

There was a security issue in all the versions of MailPoet lower to 2.6.8, this security issue was making your site highly vulnerable (blog post).

It can really only have been a plugin vulnerability as I have to manually unlock FTP access whenever I want to upload anything. So it had to be an ‘inside job’. And I had MailPoet (formerly WYSIJA) installed that account.

Outdated theme

Alternatively, it may have been a premium theme that I was using that had the Slider Revolution plugin embedded. This was reported to have a critical vulnerability last month.

I thought I had patched it…, but, perhaps with my meningitis-muddled head I didn’t do it properly.

How to clean an infected WordPress site

Whatever it was, it injected a bunch of obsfucated code into the top of all the PHP files on that site. A give away was that in the WordPress plugins screen all the plugins were disabled and reporting “the plugin does not have a valid header”.

If something similar happens to you, then you might find the following steps useful:

  1. Change passwords for:
    1. WordPress admin
    2. FTP
    3. MySQL database
  2. Backup all the files on the site. (That took ages!)
  3. Delete all WordPress core files including themes and plugins (Do not delete user-uploaded content, e.g. images, PDFs, etc.)
  4. Download clean installation of WordPress.
  5. Upload clean WordPress files (except wp-config-sample.php).
  6. Rename wp-config-sample.php to wp-config.php, update with database details and upload.
  7. Upload a clean version of your theme (remove themes that you are not using).
  8. Install and activate required plugins including antivirus and security plugins.
  9. Check other PHP files for compromise, not just WordPress files.

I found this post on the WordPress support site useful: I am getting hacked evry two weeks? Help please. There are some useful links listed on how to clean a WordPress installation.

The main lesson for me to learn from this episode is to make sure I never get viral meningitis again when there are two (or more) critical vulnerabilities in the wild!

Oh, yeah, and always keep your WordPress themes and plugins updated… and if in doubt just delete them before they can cause any problems.

Update

Sunday 19 October 2014

It looks like, based on this blog post from Sucuri WordPress Websites Continue to Get Hacked via MailPoet Plugin Vulnerability that the source of the infection was indeed MailPoet.

Uploading media to old posts in WordPress backdates the file location

From the ashes of b2/cafelog

I first started using WordPress in 2003 not long after it had been forked from b2/cafelog. It was version 0.7, before they started using jazz-inspired code names for the releases.

  • There were no pages, only posts. Pages arrived in 2005, version 1.5 Strayhorn.
  • There was no plugin architecture: if you wanted to make changes to the functionality of the application then you had to edit the core files (and re-edit them every time you updated WordPress… which was all done manually, of course). Plugins arrived in 2004, version 1.2 Mingus.
  • There were no themes. Support for that also arrived in 2005, version 1.5 Strayhorn, and was greatly improved in 2010, version 3.0 Thelonious.

The media library was also very basic in those days. You actually had to add the absolute path of the media folder in your b2config.php file, like this: $fileupload_realpath = '/home/example/public_html/images';

Organize media into folders

The media library improved over the years but one thing I never got around to switching on was this one, now found in Settings > Media: “Organize my uploads into month- and year-based folders”. I kind of wish I had now, because I have nearly 3,680 images sitting loose in /wp-content.

As part of my site migration from www.garethjmsaunders.co.uk to www.garethjmsaunders.co.uk/blog I am trying to plan the best way to move all these images into month- and year-based folders. If you have any thoughts please do leave them in the comments.

What I did discover, however, is that if I were to retrospectively upload an image today to the media library, say for a blog post dated 9 August 2007, WordPress will upload it to a directory for the month associated with the post (/wp-content/uploads/2007/08/) and not the month I actually uploaded it (May 2014).

However, the media library filter still lists the file as having been uploaded during May 2014.

According to this support ticket “#10752 Uploading new media to existing posts/pages backdates file location” this is a feature, not a bug. It would be rather nice if you could choose which convention it uses: I’d prefer to be able to filter images according to when the image was used on a post, rather than when I ‘fixed’ the image.

Workaround to get a /blog site on WordPress multisite

The following words are reserved for use by WordPress functions and cannot be used as blog names: page, comments, blog, files, feed

Last month I said that I would soon be redesigning and re-architecting my website, including this blog. It has now begun!

Losing the subdomains

Something I want to do is standardise the URLs used on the site. Once upon a time I had an idea of using subdomains for all my mini-sites, so

  • www.garethjmsaunders.co.uk
  • blueprint.garethjmsaunders.co.uk
  • mahjong.garethjmsaunders.co.uk
  • psion.garethjmsaunders.co.uk

I got as far as setting up my blog on a subdomain and I changed my mind. (Or got lazy, I can’t remember now.) 11 years later I have now decided to bite the bullet and move from www.garethjmsaunders.co.uk to garethjmsaunders.co.uk/blog. It’s potentially going to involve a lot of work (and a little .htaccess wrangling) but it will be worth it in the long run.

WordPress doesn’t like blog sites

My plan was to create a new sub-site called “blog” but when I set up a WordPress multisite installation on my local machine to test how this would all work I encountered an unexpected problem. When you try to create a new site called “blog” WordPress multisite returns this error message:

The following words are reserved for use by WordPress functions and cannot be used as blog names: page, comments, blog, files, feed

Ah!

Workaround

The workaround I worked out, however, is pretty simple:

  1. On the WordPress multisite default site, create a new page called “Blog”, with the URL of ‘/blog’. (On my localhost test site this has a URL of http://garethjmsaunders.shed/blog/.)
  2. In Settings > Reading set the posts page to be your new “Blog” page.
  3. Now import your blog into this site. (I imported it category by category, one at a time as I have a lot of posts.)

Of course, if you want your blog to use a different theme than the rest of the default site pages you will need to use a multiple theme plugin.

WordPress 3.0.1 not publishing scheduled posts

Back in May I published a post about WordPress 2.9 not publishing scheduled posts. Recently I did an automatic update to WordPress 3.0.1 … and guess what: scheduled posting has been broken once again.

I’ll try the method I used before, which was to delete all the core WordPress files and upload them again manually, but in the meantime I found this WordPress plugin has done the job: Missed Scheduled.

By default the plugin is set to run every 15 minutes, but I’ve changed mine to 2 minutes by editing line 12:

define('MISSEDSCHEDULED_DELAY', 2); // Number is in minutes, change it according to your needs

If it turns out that uploading the files manually fixes the issue again I guess I’ll be running manual upgrades in future.

WordPress 2.9 not publishing scheduled posts

On Tuesday night I scheduled a post to publish at 08:30 on Wednesday morning. It didn’t happen. Did it have a broken clock, or something?

So I tried to reschedule it. That would surely wor… No! That didn’t work either.

In fact, no matter what I did I just couldn’t get scheduled posts to publish. I would just get a message in my list of posts saying “Missed Schedule”. It turns out I’m not the only person.

I patiently went through every suggestion offered, and not one of them worked. I updated core files (http.php and cron.php) to no effect. I disabled all my plugins. Still nothing.

Other installations

In the end I installed another instance of WordPress 2.9.2 and the latest beta version of WordPress 3.0 (beta 2) and tested those. And guess what: scheduling worked!

Solution

So my solution was to backup everything (blog files and MySQL database), delete all the old files off the server and re-upload them.

Maybe, I reasoned, after the last automatic upgrade something went amiss. Maybe certain key files weren’t overwritten correctly or indeed uploaded at all.

That did the trick. I kind of wished that I’d tried that first. But you live and learn, I guess.