Skip to content
  • FG-83
  • 12 min read
  • Last checked November 7, 2026

A Plugin on Your WordPress Site Has a Security Vulnerability: What to Do

You got an alert that a plugin you run is vulnerable. Read the advisory, decide how urgent it is, patch or replace the plugin, and check whether you were already exploited.

Run the health check

An email arrives from your security plugin, your host or a monitoring service: “Vulnerability found in PLUGIN NAME, versions up to and including X.Y.Z”, often with a CVE number and a severity score. Or the plugin’s entry on your Plugins screen shows a warning, or you notice its WordPress.org page now says it has been closed. Your site may look perfectly normal.

A vulnerability alert means researchers have found a flaw in code you run, and the details are now public, which is exactly when automated attacks start. It does not mean you have been hacked. Your job is to answer three questions fast: does this affect my version and setup, how urgent is it, and did anyone use it before I patched? This guide walks you through each one. If you already see signs of a hack, start with the first-hour hacked site guide (FG-05) and come back here to close the hole.

What is happening

Plugins add code that runs with full access to your site’s files and database. When that code trusts input it should not, an attacker can send crafted requests to make it do something it was never meant to. Security researchers report these flaws to the developer and to a vulnerability database, a fix is released, and the advisory is published, usually with a short delay so site owners can update first.

Where advisories come from

  • WPScan: a WordPress vulnerability database, used by the WPScan scanner and by many hosting dashboards.
  • Patchstack: a vulnerability database and bug bounty program for WordPress, with a free plugin and alerting, and paid virtual patching.
  • Wordfence Intelligence: Wordfence’s public vulnerability database, which feeds the Wordfence plugin’s scan alerts.
  • CVE records: all three are CVE numbering authorities, so most WordPress advisories also get a CVE ID such as CVE-YYYY-NNNNN, which you can look up in the public CVE and NVD databases.
  • WordPress.org closures: when the plugin team closes a plugin, its page shows “This plugin has been closed as of DATE and is not available for download”, often with a reason such as “Security Issue”. A closure for security means no one can download it, and existing installs get no update until it is fixed and reopened.

How to read an advisory

Field What it tells you
Affected versions Usually “<= X.Y.Z”. If your installed version is higher, you are not affected by this issue.
Patched version The first safe version. “No known fix” or “Unpatched” is the worst case.
Vulnerability type What an attacker gains: cross-site scripting (XSS), SQL injection, arbitrary file upload, privilege escalation, missing authorization, remote code execution and so on.
Authentication “Unauthenticated” means anyone on the internet can attempt it. “Authenticated (Subscriber+)” means any logged-in user, including someone who just registered. “Contributor+”, “Author+” or “Administrator+” narrow it further.
CVSS score A 0 to 10 severity rating: 0.1 to 3.9 Low, 4.0 to 6.9 Medium, 7.0 to 8.9 High, 9.0 to 10.0 Critical.
CVSS vector The reasoning behind the score, for example AV:N/AC:L/PR:N/UI:N means network reachable, low complexity, no privileges and no user interaction needed.

Before you start

  • Access you need: WordPress admin, plus SSH or your host’s file manager and database tool. Access logs help with the “was I exploited” check.
  • Backup: take a full backup (files and database) before you update or remove anything. If you suspect a compromise, keep a separate copy of the current state as evidence before you change it.
  • How to run commands: connect over SSH and change into the site folder, the one with wp-config.php. WP-CLI is wp on most servers and wp-cli on some managed hosts. find and grep commands run on the server, in the same folder. If you have no SSH, your host’s file manager and phpMyAdmin can do most checks by hand, just more slowly.

Step 1: Confirm you run an affected version

wp plugin list --fields=name,status,version,update_version

Run this on the server in the site folder. --fields picks the columns: the plugin folder name, whether it is active, the installed version and the newest version WordPress.org offers. Good output: your version is above the advisory’s affected range. Bad output: your version is inside the range. A plugin with status inactive is still a risk if the flaw is in a file that can be requested directly, so do not assume deactivated means safe. An empty update_version on a plugin you know has an update usually means it is a premium plugin that updates through its own license system, or that it was closed on WordPress.org.

Also check for copies you might forget: the same plugin bundled inside a theme, a staging site in a subfolder, or other WordPress installs in the same hosting account. Attackers scan every one of them.

Step 2: Decide how urgent it is

The CVSS number is a starting point, not the whole answer. Weigh it against your actual setup:

Situation Urgency
Unauthenticated, and the type is file upload, code execution, SQL injection, privilege escalation or authentication bypass Act within hours. These are the flaws bots exploit at scale soon after disclosure.
Subscriber+ and your site allows open registration, or has customers or members who log in (WooCommerce, membership, LMS) Treat as unauthenticated: anyone can create the account they need.
Unauthenticated stored XSS High. Injected script runs when an admin views the affected screen and can be used to create a new admin.
Contributor+ or Author+ on a site where only you and trusted staff log in Update within days, during normal maintenance.
Administrator+ only Low. An attacker with admin access can already do anything.
CSRF or reflected XSS Moderate. These need you or another logged-in user to click a crafted link while logged in.

Check whether registration is open with wp option get users_can_register. Output 1 means anyone can register; 0 means only admins can add users.

Step 3: Update, replace or contain

A fix exists: update

wp plugin update PLUGIN_SLUG

Placeholder: PLUGIN_SLUG is the plugin’s folder name, shown in the name column from Step 1. Good output: a table showing the old and new version and “Success: Updated 1 of 1 plugins.” Bad output: “Warning: … could not be updated” or a premium plugin that will not update; update it from its own settings screen or download the new zip from the vendor’s account area. Then load the front end and a few key pages to confirm nothing broke. If an update breaks your page builder, the builder guide (FG-07) covers recovery.

No fix exists: deactivate, or replace

If the advisory says unpatched and the risk is high, deactivate the plugin now and live without that feature for a while:

wp plugin deactivate PLUGIN_SLUG

Deactivation stops the plugin’s hooks from running, which closes most flaws. It does not stop direct requests to PHP files inside the plugin folder that run code on their own. For an unauthenticated flaw in such a file, or a plugin that has been closed for a security issue with no sign of a fix, delete it with wp plugin delete PLUGIN_SLUG and choose a maintained alternative. Deletion removes the plugin folder; its settings usually stay in the database unless the plugin cleans up after itself.

Virtual patching while you wait

A virtual patch is a firewall rule that blocks requests matching the attack before they reach the vulnerable code. Patchstack and Wordfence both ship rules for specific WordPress vulnerabilities (Wordfence’s free tier receives new firewall rules after a delay; the paid tier gets them in real time). Cloudflare’s managed WAF rulesets cover some high-profile issues. You can also write a narrow Cloudflare custom rule yourself when the advisory names a unique URL, such as a plugin file or REST route. For example, to block anonymous access to a vulnerable REST namespace:

(starts_with(http.request.uri.path, "/wp-json/PLUGIN_NAMESPACE/") and not http.cookie contains "wordpress_logged_in_")

Placeholder: PLUGIN_NAMESPACE is the route prefix named in the advisory (for example the part after /wp-json/). Create it in Cloudflare under Security, then Security rules, then Create rule, then Custom rules, and paste it with Edit expression, with the action Block. The cookie check is a convenience filter, not authentication, because an attacker can send any cookie, so this only helps against unauthenticated mass scanning. Rules on Cloudflare’s lower plans cannot inspect POST bodies, so an admin-ajax.php action sent in the body cannot be matched this way. Virtual patching buys time; it never replaces updating or removing the plugin.

Step 4: Check whether you were already exploited

Look back to at least the advisory’s publication date, and ideally to when the vulnerable version was installed. What to look for depends on the vulnerability type.

Privilege escalation, authentication bypass, stored XSS: new or changed users

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --orderby=registered --order=DESC

This lists administrators, newest first. Good output: only accounts you know, none registered recently without a reason. Bad output: an unfamiliar admin, especially one created after the vulnerable version was installed, or a generic name like wpadmin or support. Also check that existing low-level users were not promoted, by running the command without --role and scanning the list.

Option changes

wp option get users_can_register
wp option get default_role
wp option get siteurl
wp option get home
wp option get admin_email

A common privilege escalation payload switches on registration and sets the default role to administrator, so any visitor can sign up as an admin. Good output: users_can_register as you set it, default_role as subscriber (or customer on WooCommerce), and URLs and admin email that you recognize. Bad output: default_role of administrator, an unfamiliar admin email, or a site URL pointing somewhere else (the malicious redirect guide, FG-23, covers that case).

Arbitrary file upload and code execution: new PHP files

find wp-content/uploads -type f -name "*.php*"
find . -type f -name "*.php" -newermt YYYY-MM-DD -not -path "./wp-content/cache/*" | head -50

Placeholder: YYYY-MM-DD is a date shortly before the vulnerable version was installed or the advisory was published. What they do: the first lists any PHP file in the uploads folder, which normally holds only media; "*.php*" also catches tricks like file.php.jpg handled oddly by some servers. The second lists PHP files modified after the date, skipping a cache folder. Good output: nothing in uploads (a few plugins write legitimate index.php guard files, which contain only a comment), and recent changes that match updates you ran. Bad output: random names, files in date folders like uploads/2026/11/, or changed files in plugins you did not update. Modification times can be faked, so a clean result here is reassuring, not proof. If a scanner flags a file and you are unsure, the false positive guide (FG-86) shows how to judge it.

SQL injection and XSS: injected content

wp db query "SELECT option_name FROM $(wp db prefix)options WHERE option_value LIKE '%<script%' LIMIT 20;"
wp post list --post_type=any --orderby=modified --order=DESC --fields=ID,post_title,post_modified --posts_per_page=20

The first searches the options table for script tags; $(wp db prefix) fills in your table prefix (often wp_) automatically. Some legitimate options (analytics or header code plugins) contain scripts, so review each hit rather than deleting. The second lists the most recently modified content. Bad output: option values or posts with script tags you did not add, or posts changed at times when no one was editing.

Access logs: requests to the vulnerable endpoint

If the advisory names the file, AJAX action or REST route, search your logs for it:

grep -i "VULNERABLE_PATH" ACCESS_LOG | awk '{print $1, $4, $6, $7, $9}' | head -50

Placeholders: VULNERABLE_PATH is the file name, action name or route from the advisory; ACCESS_LOG is your access log path (often under /var/log/nginx/ or /var/log/apache2/, or the control panel’s raw access logs). The awk prints IP, time, method, path and status. Bad output: POSTs to that endpoint returning 200 from IPs that are not yours, especially just before a new admin or file appeared. If you find signs of exploitation, stop here and follow FG-05, then FG-26 to make sure it does not come back.

Step 5: Deal with abandoned and closed plugins

A plugin that has not been updated in a year or more, or that WordPress.org lists as “not tested with the latest 3 major releases of WordPress”, may still work, but no one is likely to fix the next flaw. Check each plugin’s WordPress.org page for its last update and closure notices. For premium plugins, check the vendor’s changelog.

When a plugin is closed or abandoned, plan a replacement before the next advisory forces it on you. Choose an alternative that is actively maintained, has a clear changelog, and responds to support threads. Test the swap on a staging copy, migrate any settings or content, and delete the old plugin entirely rather than leaving it deactivated.

Advanced: triaging advisories across many sites

If you manage a fleet of sites, a single advisory can affect dozens of installs. Script the version check across every site you can reach over SSH:

for d in /PATH/TO/SITES/*/; do
  v=$(wp plugin get PLUGIN_SLUG --field=version --path="$d" 2>/dev/null)
  [ -n "$v" ] && echo "$d $v"
done

Placeholders: /PATH/TO/SITES/ is the parent folder holding each site’s WordPress root; PLUGIN_SLUG is the plugin folder name. --path points WP-CLI at each install, --field=version prints only the version, and 2>/dev/null hides errors from folders without that plugin. Output: one line per site that has the plugin, with its version, so you can update the affected ones first. Management dashboards (MainWP, ManageWP and similar) and vulnerability feeds with API access do the same at larger scale.

Prioritize by exposure, not by alphabet: sites with open registration or WooCommerce customer accounts first, high-traffic sites next, brochure sites with one admin last.

When to contact your host

Open a ticket if you cannot update or remove the plugin, if you found signs of exploitation, or if you want firewall-level blocking while you work. Include:

  • The plugin name, your installed version and the advisory link or CVE ID.
  • What you have done so far (updated, deactivated, deleted) with UTC times.
  • Any suspicious users, files or log lines, with full paths and timestamps.
  • Whether other sites in the account run the same plugin.

Many hosts run server-side malware scanning and WAF rules and can tell you whether the vulnerable endpoint was hit. Our diagnostic tools help you check your site from outside; if the site is already defaced or redirecting, go to the Emergency Room.

Still stuck?

Print a free one-page checklist to keep, or open the Emergency Room if the symptom does not match.

Get the free checklists

Keep reading

All field guides
FG-116 Security

Scary Website Emails: Beg Bounties, Fake DMCA Notices and Renewal Scams

An email says your site has a vulnerability, broke copyright, is about to lose its domain or will be suspended. Most are scams, some are real. Verify each one by logging in to the real account yourself, never through the link in the email.

Read the guide
12 min read
FG-05 Security

WordPress Site Hacked? What to Do in the First Hour

Contain it, preserve evidence, find how they got in, then rebuild from clean sources and rotate every credential. The order matters, and skipping a step is how sites get reinfected.

Read the guide
11 min read

Search the field manual

What is going wrong?

Press / to search from any page, Esc to close