WordPress Plugin Backdoors: How to Audit a Site That a Security Plugin Already Vouched For

··12 min read
WordPress Plugin Backdoors: How to Audit a Site That a Security Plugin Already Vouched For

A security plugin's green checkmark feels like good news. You installed it, it ran a scan, the dashboard says "No threats found," and you move on. The problem is that a scanner and a backdoor are often playing different games. A signature-based scanner looks for known malware patterns. A deliberate backdoor, especially one planted by someone who knew the scanner's rules, is designed to look like ordinary WordPress code. Many of the compromises that surface months later were sitting quietly on sites that had passed a scan with flying colors.

Here is the uncomfortable number. Wordfence and Sucuri have both published years of incident data showing that a large share of compromised WordPress sites were infected through plugins or themes, not WordPress core, and that many infections persisted through routine scans because they were obfuscated, stored in the database rather than the filesystem, or hidden in files the scanner whitelisted. The exact percentages shift from report to report, so treat any single figure with caution. The pattern itself is consistent: the scanner was not wrong about what it checked, it just wasn't checking the right things.

This article is about the gap between "the plugin says we're clean" and "we are actually clean." You will learn what a WordPress plugin backdoor check really involves, how backdoors hide from automated tools, and a repeatable manual audit you can run in an afternoon on a site a security plugin has already signed off. We will also compare the methods worth using, walk through a worked example with real numbers, and be honest about where manual review stops being worth the time.

Key Takeaways

  • A clean scan result means "no known signatures matched." It does not prove the site is free of backdoors, especially custom or obfuscated ones.
  • Backdoors commonly live in wp-includes, wp-content/uploads, theme functions.php files, the wp_options table, and mu-plugins.
  • Compare core, plugin, and theme files against known-good copies from official sources using checksums, not against the site's own backups.
  • Search for dangerous functions like eval, base64_decode, and assert in context, then verify each hit by hand.
  • Check for unauthorized administrator accounts, scheduled tasks, and outbound connections, since many backdoors are identified by their behavior, not their code.
  • Rotate every credential the site touches after you clean it, or you may simply reopen the door you just closed.

What Is a WordPress Plugin Backdoor, Exactly?

A backdoor is any code that gives someone access to a site that the site owner did not intend to grant. On WordPress, that usually means a way to execute arbitrary PHP, create an admin user, or upload files, without going through the normal login.

Backdoors fall into a few broad families:

  • Webshells: A single PHP file, often named to look legitimate (wp-config-sample.php, class-wp-cache.php), that accepts commands through a request parameter.
  • Injected code: A few lines appended to a legitimate file such as functions.php or a plugin's main file, usually obfuscated.
  • Rogue admin accounts: A user with administrator rights and an innocuous name, sometimes with a hijacked email address.
  • Database persistence: Serialized payloads in wp_options or wp_posts that re-create files if you delete them.
  • Scheduled reinfection: A WP-Cron event or system cron job that quietly restores the malware every few hours.

The last two are why "I deleted the file and it came back" is such a common story. If you only remove the visible payload, the infection regenerates.

Why Does a Security Plugin Miss Backdoors?

Most security plugins combine a few techniques: signature matching against known malware, file integrity checks against WordPress.org checksums, and sometimes heuristic rules that flag suspicious functions. Each has blind spots.

The four main blind spots

  1. Custom or fresh code. A backdoor written for one target has no public signature. Signature scanners cannot flag what they have never seen.
  2. Heavy obfuscation. Nested base64_decode, str_rot13, and variable-variable tricks can make a payload look like random noise to a rule set tuned for common patterns.
  3. Whitelisting and excluded paths. Some scanners skip wp-content/uploads or trust files in certain directories to avoid false positives. Attackers know this.
  4. Non-file persistence. A payload stored in the database, or delivered through a remote option, never touches the filesystem where a file scanner looks.

A scanner's "clean" result is therefore a claim about what was checked, against what rules, at what time. Your job is to test the claim.

How Do You Run a WordPress Plugin Backdoor Check Manually?

The most useful manual audit combines four passes: integrity verification, suspicious-code search, account and scheduler review, and behavior monitoring. Work through them in order and log what you find. Do not start by deleting anything.

Step 1: Snapshot before you touch anything

  1. Take a full copy of the site files and a database export. Store them offline, labeled with the date. You may need them for forensics.
  2. Note the WordPress version, PHP version, and the list of active plugins and themes from the Dashboard under Plugins and Appearance.
  3. Record the output of wp core version and wp plugin list --format=csv if you have WP-CLI, so you have a baseline.

Step 2: Verify core files against official checksums

WordPress publishes checksums for every core release. Run this from the site root:

  1. Run wp core verify-checksums. Any file reported as modified, or any unexpected file in a core directory, needs investigation.
  2. Run wp core verify-checksums --version=X.Y.Z --locale=en_US if the automatic version detection is unreliable.
  3. Note that wp-content is excluded from core checksums by design, so this step only covers core.

Step 3: Verify plugins and themes against their source

  1. For each plugin on WordPress.org, run wp plugin verify-checksums --all. This compares files against the published plugin archive.
  2. For premium plugins and themes with no public checksum, download a fresh copy from the vendor and diff it against the installed version. A plain diff -rq works well.
  3. Pay special attention to files that exist in the installed copy but not in the vendor package. Those extra files are a classic backdoor pattern.

Step 4: Search for dangerous functions, then read the hits

Search the whole site, including wp-content/uploads, for these patterns:

  • eval(
  • base64_decode(
  • gzinflate( and gzuncompress(
  • str_rot13(
  • assert(
  • preg_replace with the /e modifier (legacy, but still seen)
  • system(, exec(, shell_exec(, passthru(
  • $_REQUEST or $_POST passed directly into any of the above

A grep command such as grep -rIl --include="*.php" "eval(\|base64_decode(" . gives you a file list. Many legitimate plugins use base64_decode for ordinary data, so do not stop at the match. Read the surrounding lines and ask: does this code take input from a request? Does its output run as code?

Also check any PHP file inside wp-content/uploads. Legitimate uploads are images, PDFs, and media. A .php file there is almost always a problem.

Step 5: Audit user accounts and roles

  1. In the Users screen, sort by registration date and look for administrators you do not recognize.
  2. Check each admin's email address. Attackers often use a throwaway address they control.
  3. Run wp user list --role=administrator --fields=ID,user_login,user_email,user_registered for a clean table.
  4. Review application passwords and any API keys under each user profile.

Step 6: Inspect the database and scheduled jobs

  1. Run wp cron event list and look for events you did not create, especially ones with unfamiliar hook names.
  2. Check wp_options for very large serialized values or option names that look random. Query for autoloaded options over a few hundred kilobytes.
  3. Check the server crontab with crontab -l for the web user, and inspect mu-plugins, which load automatically and are a favorite hiding spot.

Step 7: Watch behavior for 48 hours

Some backdoors reveal themselves only when they call home. Log outbound connections from the server, and review web server access logs for requests to unfamiliar PHP filenames, especially POST requests with long parameter strings.

Worked Example: Auditing a Site With 47 Plugins

Consider a small agency site, which we will call Site A. It runs WordPress 6.x with a child theme and 47 active plugins across 12 client-facing features. The owner runs a security plugin weekly and it has reported zero threats for three months. A client complains that search results are redirecting to an unfamiliar domain, which is a common sign of a redirect injection.

Here is how a manual pass plays out, with rough numbers from a typical audit:

  • Step 2 (core): 0 modified files out of roughly 1,500 core files. Core is clean.
  • Step 3 (plugins): 44 of 47 plugins verify cleanly. Three plugins, including one abandoned premium extension, show 2 modified files each.
  • Step 4 (grep): The search returns 61 files matching base64_decode(. Reading them down to the ones that take request input, 3 remain suspicious. One is a .php file inside wp-content/uploads/2024/ that looks like a cache file.
  • Step 5 (users): One administrator, registered 41 days earlier, uses a free email domain and a name that resembles a team member's.
  • Step 6 (cron): An unfamiliar hook runs every 15 minutes and re-writes a file in the theme folder.

Total time: about four hours for an experienced person. The scanner had reported nothing because the uploads file was obfuscated, the modified plugin files were in a directory the scanner excluded, and the cron job

Cover image: Innovate Maryland Emerging Technology Center by MDGovpics, licensed under BY 2.0 via Openverse.

Recent Posts

View all →

Most Popular Software

View all →

Browse by Platform

View all →