Not too long ago, I helped a friend clear out some malware on their WordPress site. In doing so, we went thru a pretty comprehensive site cleanup checklist:
- Deleted unused plugins & themes.
- Installed a fresh copy of everything.
- WordPress core
- Double checked the .htaccess file.
- Changed every conceivable password.
- WordPress admin users
- FTP/SFTP/SSH account
- MySQL user
- Bought a SSL certificate and forced all wp-admin & wp-login.php traffic to it.
- Reset their WordPress security keys.
- Disabled the plugin & theme editor.
But the malware would keep getting reinserted in the site. Almost like clockwork. So I had them install a logging plugin to see if we could catch what was happening. Sure enough, the lone admin account would log in, upload & activate a plugin — which seems to be how the attacker was delivering their payload — and then remove the plugin from the site before logging out. Running a “stat” (via the shell) on the files we’d cleaned previously all showed that the modify timestamp matched up (roughly) with the plugin activation timestamp.
Because this kept happening, there were some concerns that my friend’s personal Gmail account might be hacked. They enabled 2-step verification as a response and started watching the account activity like a hawk.
Then the malware came back. Again. But nobody had logged in to my friend’s Gmail account. Things were getting to be maddening.
Since we couldn’t spot anyone logging into their email account, I started going over their Apache logs. That’s when I found something interesting…
If you look at things closely, you’ll see that the
key value is the same both times. And, upon checking their database, I noticed the key value was still set to
HR9qFJ2N6c3XveQY487b. Since WordPress clears out the
user_activation_key value from the database after it has been used, the faux plugin the attacker was using must’ve pushed that value back into place to provide them with a backdoor. Pretty sneaky.
So I cleared the malware, locked the site down again and made sure to clear out the
user_activation_key. Sure enough, the malware stopped coming back and my friend was happy that they could finally concentrate on blogging again.
Anyhow, ever since then, this little MySQL query has been getting run on every hacked site I’ve worked on…
Yeah, that totally guts all of the
user_activation_key values for admin and non-admin users alike — and I’m sure that most attackers aren’t as clever as the one I was dealing with — but it’s always better to be safe than sorry, right?