Nobody plans for the phone call, but if you run a WordPress agency long enough, you will get it: a frantic client, a mangled admin dashboard, and a site that has been quietly serving malware for days. This is the story of one such compromise — a wp2shell-style backdoor campaign — and the disciplined incident-response playbook our team used to clean, harden, and recover a client site in under 48 hours.

I am going to share the mechanics of what we found, why the attacker designed their backdoor the way they did, and exactly how we approached the cleanup from a maintenance and security specialist’s perspective. This is not a scare piece. It is a working reference you can apply the next time you suspect a compromised WordPress install.

What an Attacker Leaves Behind: The Anatomy of a Modern Backdoor

The site we inherited had been breached roughly ten days before we were engaged. On the surface nothing looked wrong — the homepage loaded, the shop worked, and the client had been too busy to notice the gradual slowdown and the odd new user in the admin panel. Underneath, the compromise was deep. Here is what a complete, professionally deployed backdoor looks like in practice.

1. The “Ghost Admin” User Accounts

The most common entry artifact in these campaigns is a set of hidden administrator accounts created automatically and silently. Attackers name them to look innocuous — a mixture of random-looking tokens and names that could pass as a contractor. In our case there were half a dozen of them, all created within a two-day window. Each had full administrator privileges, which meant the attacker could log in, install plugins, edit themes, and upload webshells at will.

Why create admin users instead of just planting a file? Because a user account survives most theme and plugin updates, hides in plain sight among other legitimate users, and gives the attacker a persistent, credential-based way back in even after individual files are cleaned.

2. Backdoored Core Files

The attacker had also modified core WordPress files — specifically the entry points every scanner skips because they are supposed to be “known good.” The index.php file had been replaced with truncated, obfuscated code: a compact blob using gzinflate(base64_decode(...)) wrapped around every loop. The wp-login.php file had ballooned from a few kilobytes to over fifty — a strong signal of injected code.

This is the signature of a lazy-but-effective backdoor: obfuscation over elegance. The attacker does not need the payload to be readable, only executable. Compression plus base64 plus recursion makes the code nearly impossible for a human or a basic scanner to eyeball.

3. Webshells Hiding in the Uploads Folder

Webshells — small PHP files that give the attacker a remote command shell — were tucked into the wp-content/uploads/ directory, buried inside dated month folders alongside legitimate media. One of them was multi-layered obfuscated and over sixty kilobytes, designed to look like a corrupted image or a partial theme file at a glance. This is a favourite hiding spot because uploads folders are typically excluded from malware scans and are writable by nature.

4. Suspicious Archives

We also found several encrypted or suspicious .zip archives sitting in the uploads tree. Archives in an uploads directory are a red flag by themselves — they are almost always staged payloads or exfiltration bundles waiting to be triggered or downloaded.

5. The Log4Shell Pivot Attempt

The most sophisticated finding was a Log4J / Log4Shell attack attempt directed at the server. Log4Shell (CVE-2021-44228) is the remote-code-execution family of vulnerabilities in Apache Log4j. Attackers probe for it by injecting ${jndi:ldap://...} payloads into fields, hoping to trigger outbound connections to a command-and-control server. We observed a JNDI-injection attempt in the server logs — evidence that the attacker was probing for additional, more privileged footholds beyond the web application itself.

This is the tell-tale sign of a campaign, not a one-off hack. The operator had a toolset, a checklist, and a lateral-movement mindset.

Our Cleanup Playbook: The Order That Matters

When you discover a compromise, the instinct is to start deleting things. Resist it. Forensics before cleanup is the single most important discipline — if you wipe evidence first, you will never know how they got in and you will almost certainly miss something they left behind.

Step 1: Contain — Lock the Perimeter

Before touching a single file, we disconnected the site from automated systems that could cause re-infection: we suspended the cron schedule, disabled XML-RPC (a common brute-force and scanning vector), and blocked the known attacker IP ranges at the firewall. We also reset passwords for every administrative user and rotated credentials for any connected service — email, payment gateway, FTP, and CDN accounts. A backdoor is only useful if the attacker still has a way in, so we made sure every known door was re-keyed.

Step 2: Collect Evidence — Do Not Skip the Forensics

We exported the following before changing anything:

  • Full file listing with timestamps, sorted by modification time to spot the burst of files created around the breach window.
  • Complete user table (wp_users + user meta) to identify every account and when it was created.
  • Active plugins and themes, including ones that were installed but hidden from the menu.
  • Access logs around the incident window to map the attacker’s activity.
  • Database export for later analysis and clean-state comparison.

Every decision afterwards was made against this evidence baseline, which dramatically reduced our “did we miss something?” anxiety.

Step 3: Eradicate — Clean the Files

We removed the malicious admin accounts and moved their associated content to the legitimate site owner. Then we deleted every webshell and suspicious archive from the uploads directory, and — critically — we restored the modified core files from fresh WordPress core archives rather than trying to surgically patch them. When a core file has been obfuscated, the safe move is not to reconstruct it byte-by-byte; it is to replace the wp-login.php, index.php, and any other touched core files with clean, version-matched originals from the WordPress repository.

The same logic applies to themes and plugins: any file that had been modified was replaced with a clean copy from the official source, then updated to the latest version. We treated nothing on that install as trustworthy until it was restored from a known-good source.

Step 4: Harden and Rebuild Confidence

Once the install was clean, we re-secured it rather than returning it to its previous, weaker state. Concretely, we activated the security plugin that was installed-but-inactive (the attacker had disabled it — a classic move), enabled a web-application firewall at the network edge, and reviewed WordPress core hardening recommendations: blocking direct access to sensitive files, disabling file editing in the dashboard, and limiting login attempts against brute force.

We also scanned the other sites on the same server for cross-contamination. Backdoor campaigns rarely stop at one site, and a “known breached” neighbour is the fastest highway back into a freshly cleaned install.

Why the Entry Vector Is the Hardest Question

Here is the honest, uncomfortable truth of incident response: in many cases you can clean a site completely and still never prove exactly how the attacker got in. We identified the backdoors, removed them, and hardened the perimeter — but the initial breach could have come from any of several angles: an out-of-date plugin with a known vulnerability, a reused or leaked credential, a weak password, or a vulnerability in a third-party integration we could not fully trace.

We noted several plugins on the site that were notably out of date at the time of the breach. Out-of-date plugins are the single most common root cause of WordPress compromises, and they were the most plausible entry point in this case. We flagged every outdated plugin for an immediate update as part of the remediation, and recommended the client rotate credentials for any system that shared a password with the site — because recycling credentials is how a single breach becomes a network-wide one.

A good security specialist will tell you when a determination is uncertain. Selling false certainty (“we know exactly how it happened”) is what damages trust far more than admitting the residual unknowns. WordPress documents its own post-hack checklist, and every serious guide — Wordfence’s included — lands on the same point: assume the worst, clean everything, harden everything, and then watch.

The Maintenance Mindset: Preventing the Next Incident

The cheapest security measure in WordPress is not a firewall. It is an update discipline. The vast majority of backdoor campaigns — including this one — are enabled by out-of-date software with known, public vulnerabilities. From a maintenance specialist’s point of view, the post-incident state of this site was almost more revealing than the malware itself:

  • Outdated plugins — sitting on known-vulnerable versions.
  • A disabled security plugin — deactivated, ironically, right around the time of the breach.
  • No network firewall — so the site had no early-warning layer between attackers and the application.
  • Reused / weak credentials across services.

If you manage WordPress sites for clients — or even just for yourself — take the following as non-negotiable hygiene:

  1. Keep WordPress core, every theme, and every plugin on the latest stable version and run updates on a fixed schedule.
  2. Run a malware scanner on a recurring basis, not just after an event.
  3. Use a web application firewall at the network edge, not just in the plugin.
  4. Enforce strong, unique passwords and rotate them for any account that touches the site.
  5. Disable XML-RPC unless you genuinely need it, and remove file editing from the WordPress admin.
  6. Keep a clean, verified backup you can restore from in minutes — this is your last line of defence.

Key Takeaways

A wp2shell-style backdoor is not an exotic threat — it is a common, well-tooled campaign that preys on the same weakness every time: maintenance neglect. Here is what to remember:

  • A compromise often announces itself through mystery admin accounts before anything visibly “breaks”.
  • Backdoored core files and webshells in the uploads folder are the durable parts of the attack — find and replace them from clean sources.
  • Forensics before cleanup. Evidence is vanishing; once it is gone you can never recover the answer.
  • Clean everything, including neighbouring sites on the same server, then harden and re-scan.
  • Be honest about uncertain entry vectors — integrity beats false reassurance.
  • The real fix is a maintenance discipline: updates, scanning, firewalls, unique credentials, and restorable backups.

If you manage multiple client sites, the lesson scales up: you are not just building websites, you are operating them, and a client’s worst security day is the day your maintenance discipline is tested.

Additional Reading

This article grew out of real incident-response work at CODE TỐT, where we run proactive security audits across the WordPress projects we maintain. For a deeper, tool-by-tool look at how to audit a WordPress site before anything goes wrong, read our guide on running an automated WordPress security audit on RunCloud.

Image credit: cybersecurity illustration via Unsplash.