They didn’t hack in. They logged in.

27 Aug 2026

Hacker approaching an open door

A side effect of hosting WordPress websites is that lots of website owners, designers and developers each have admin login accounts to their various sites. It only takes one of these to make a mistake with their password hygiene, and I’ll end up having a bad day. I’ve seen this several times over the last few months, where hackers didn’t break in to a site, they simply logged in using admin credentials they’d stolen from who-knows-where.

It falls to me to clean up the mess.

How website attacks start

When a hacker logs in to a WordPress account using stolen credentials, the first thing they seem to do is change all the admin account passwords. This immediately raises a notification on our systems. It’s not an alarm, as such (administrators are allowed to change their passwords, of course). But when you see all admin accounts, for a single site, have their passwords reset within the space of two seconds, something’s amiss.

That brought me to the table around the same time the hacker was trying to do their damage.

This was one of those simple hacks where a small code snippet injects an iframe that overlays the site, showing something like a casino website instead. They also created multiple Russian-language spam blog posts, referencing casinos.

Overall, the payload was manageable. But I had to stop them as soon as possible, in case they wanted to install nastier components.

Stopping the hacker in their tracks

The core tool here was WP-CLI, accessing the site from the terminal. This was crucial, because I couldn’t trust the web interface until I’d finished cleaning up the site. I don’t want my admin browser sessions being hijacked.

The sequence had to look something like this:

  1. Put the site into maintenance mode.
  2. Scramble all Administrator and Editor user account passwords immediately using wp user reset-password <user>.
  3. Void all current logged-in sessions, to log out anyone who is already logged in.
  4. Delete all WordPress Application Passwords.
  5. Check WP core file hashes to see if any core files had been changed.
  6. Scan the site for malware.

The interesting item here is forcing all users to log out by voiding all sessions. Rather than going to the database and messing about with the sessions tables, or even hand-editing wp-config.php, you can scramble the AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY and the rest of the WordPress salts in one go with WP-CLI:

wp config shuffle-salts --skip-plugins --skip-themes

By scrambling all the keys, every currently logged-in session is invalidated, including the hacker’s. Skipping the plugins and themes is important here, because we’ve not scanned wp-content/ for malware yet.

Looking at the admin user accounts, the hacker had created an Application Password called sentinel. This was their back door. Application Passwords authenticate directly against the REST API and don’t go anywhere near the normal login form, so resetting the account password and shuffling the salts does nothing to close them off. Even with every other door locked, the hacker would still have been able to access and manipulate the site via the REST API until these were removed. For each admin and editor user ID, for example 1, 4, 7, 9 and 14:

wp user application-password delete 1 --all
wp user application-password delete 4 --all
wp user application-password delete 7 --all
wp user application-password delete 9 --all
wp user application-password delete 14 --all

Time to check the logs and make sure the hacker was no longer in the site. Following the site’s access logs, I could see them hitting wp-login.php and failing to get in. The multiple failed login attempts put them on the fail2ban blocklist and their traffic stopped completely.

Making progress!

Cleaning up the mess

To look for damage, the simplest thing to do is check for posts created in the last hour or so. But post publication dates can be faked. That said, this was a fairly quiet site in terms of new content, so wp post list showed the new Russian-language posts right away. Delete, delete, delete. Flush the caches.

A quick call to wp core verify-checksums confirmed that no core files had been altered (phew). Then I just ran our in-house malware detection tools over the site to find the bad code. There was a custom single-file plugin sitting in mu-plugins/, delete that thing. We also found something in plugins/. Neither the theme nor any of the existing plugin files had actually been touched. All they’d done was upload a new plugin of their own, a small file-dropper that fetched the real payload and installed it. A few file deletions and a couple of hours of log-file monitoring, and we were home free.

Oh, and it was time to install our WordPress 2FA plugin, for good measure. If the hackers only had the website credentials (without access to the web designer’s mailbox), this would’ve stopped today’s attack at the front door.

The final bit of the clean up was in the real world. I reached out to the web designer to inform them of what had happened, and that they should run some in-depth anti-malware scans at their end (and stop emailing passwords to people).

In short…

Treat your password like your toothbrush. Don’t let anybody else use it, and get a new one every six months.

Clifford Stoll

Leave a comment