WordPress security audit

WordPress security audit

Last verified: October 15, 2026
14 min read
Case study
Security auditor

#WordPress security audit of a small business site

A small-business WordPress site came in for a security audit, and the findings were the ordinary kind that quietly put a site one automated scan away from compromise. The page builder, Elementor, was pinned at version 3.11.1, below the fixes for five CVEs listed in NVD, including SQL injection and cross-site scripting. Contact Form 7 was running 5.8, exposed to CVE-2023-6449, an arbitrary file upload. Nothing exotic, just outdated plugins, which is the dominant way WordPress sites get breached.

The uncomfortable part is how normal this is. The site worked. It looked fine. The owner had no reason to suspect anything, because outdated plugins do not show symptoms until they are exploited. The audit existed to find the gap before someone else did.

#Why outdated WordPress plugins are a security risk

A plugin is safe while it is patched. The moment a vulnerability is published as a CVE and you have not updated, the exploit is public knowledge and your installed version is a named target for automated scanners. Attacks on WordPress are overwhelmingly blind and automated. Bots sweep the network for known-vulnerable versions of popular plugins, so running an old Elementor or Contact Form 7 is not an abstract risk. It is an advertised one.

On this site:

  • Elementor 3.11.1 sat below the fixes for five CVEs listed in NVD: SQL injection (CVE-2023-0329, fixed in 3.12.2), missing authorization (CVE-2023-33922, fixed in 3.13.3), cross-site scripting (CVE-2023-47505) and improper authentication (CVE-2023-47504, the one NVD scores critical at 9.8), both affecting versions through 3.16.4, and a dangerous file upload (CVE-2023-48777, fixed in 3.18.2). Every one of those five was live on production.
  • Contact Form 7 5.8 was exposed to CVE-2023-6449, an arbitrary file upload flaw in the plugin itself (versions up to and including 5.8.3) that let an authenticated user with editor rights upload arbitrary files. On its own it rarely leads to code execution, but chained with a second flaw such as local file inclusion it can.

Neither plugin was unusual. Both sit on a huge share of WordPress sites. That is exactly why their known vulnerabilities are worth a scanner’s time. Patchstack’s State of WordPress Security in 2026 report measures the median time from public disclosure of a high-impact vulnerability to mass exploitation at five hours. The window between “CVE is public” and “a bot is already scanning your version” is measured in hours, not weeks.

The same Patchstack report counts 11,334 new vulnerabilities in the WordPress ecosystem in 2025: 91 percent in plugins, 9 percent in themes, and only 6 reports in WordPress core, all of them low priority. An audit that stops at the core version checks the smallest part of the attack surface.

Abandoned plugins make this worse. When the author stops shipping fixes, a CVE closes only by removal or by swapping the tool. Leaving a dead plugin “just in case” in wp-content/plugins/ keeps the attack surface alive even after deactivation, if the files still sit on disk and remain reachable via known paths.

#How to prioritise WordPress plugin CVEs

The WordPress admin shows updates alphabetically or by date. Security triage sorts differently:

  1. Severity and attack class. File upload, remote code execution and SQL injection go before XSS that needs a logged-in admin.
  2. Required privileges. An unauthenticated flaw, or one reachable as subscriber/editor, ranks above one that needs an administrator you have one of and protect with 2FA.
  3. Whether the vulnerable code path is in use. A CVE in an optional feature only matters on sites where that feature is reachable. The audit checks whether the affected function is actually used on the site, not only whether the plugin is installed.
  4. Whether a public exploit exists. When a proof of concept circulates in threat intel, response time shrinks to hours. Patchstack also notes that 46 percent of vulnerabilities have no patch at disclosure, so sometimes the only immediate answer is to disable the feature or the plugin, not “click Update”.

On the site in this case, triage was straightforward: Contact Form 7 first (upload), then Elementor (injection and XSS), then cleanup of remaining plugins without an active CVE. The CVE number and publish date do not replace that order.

#How to read the CVSS score of a WordPress plugin CVE

Vulnerability databases attach a CVSS base score to most CVEs. The CVSS v3.1 specification from FIRST maps the number to a rating: 0.0 is None, 0.1 to 3.9 Low, 4.0 to 6.9 Medium, 7.0 to 8.9 High and 9.0 to 10.0 Critical. The score is built from metrics such as Attack Vector, Attack Complexity, Privileges Required and User Interaction, and the vector string printed next to the number says more about your site than the number does.

CVE-2023-6449 shows why. The NVD entry carries two CVSS 3.1 scores for the same flaw:

Scored byVectorScore
Wordfence (CNA)AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H6.6 Medium
NVDAV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H7.2 High

The only difference is Attack Complexity. Wordfence rated it High, which in the FIRST definition means success depends on conditions outside the attacker’s control; NVD rated it Low. That one metric moves the rating from Medium to High. Both agree on PR:H, high privileges, the level FIRST describes as significant control such as administrative rights. For triage that metric is the useful part: on a site with one editor account protected by two-factor login, the practical risk is lower than on a site where a dozen editor logins are shared between staff, an agency and freelancers.

FIRST also advises consumers of CVSS to supplement the base score with temporal and environmental scores. The CVSS v4.0 specification, published on 1 November 2023, notes that providers such as product maintainers and the NVD usually publish only base scores, labelled CVSS-B. The environmental part, how exposed the vulnerable feature is on your particular site, stays the auditor’s job.

#Who assigns a CVE ID to a WordPress plugin vulnerability

CVE IDs are assigned by CVE Numbering Authorities (CNAs): vendors, researchers, CERTs and other organisations that the CVE Program authorises to assign IDs and publish CVE records within their own scope. Patchstack, Wordfence and WPScan are all on that partner list as CNAs. The cve.org record for CVE-2023-6449 names Wordfence as the assigner: the ID was reserved on 30 November 2023 and published on 1 December 2023. The record lists Contact Form 7 versions up to and including 5.8.3 as affected and links the code change between 5.8.3 and 5.8.4 as the fix. The NVD then adds its own analysis on top, which is where the second score in the table comes from.

For an audit, the vendor, the CNA and the NVD publish at different moments and can score differently. One scanner showing Medium and another High for the same plugin version is not a conflict to settle by picking the lower number. The audit records the CVE ID, the affected version range from the record and the first fixed version, and compares that range with the version actually installed.

#How to update WordPress plugins safely on staging

Jumping Elementor from 3.11.1 to the 3.18+ line is not only a security patch. It changes renderers, widgets and often child-theme templates. A contact form with an old upload shortcode can throw a white screen after update or silently drop attachments. That is why remediation after the audit looked like this:

  • Full copy of files and database to staging with the same PHP and the same constants in wp-config.php (without production WP_DEBUG_DISPLAY).
  • Update plugins with CVEs in triage order, then a manual pass: homepage, cart or lead form, Elementor edit screen, form submit with attachment.
  • Compare PHP logs and HTTP 500s before pushing to production.
  • On production, the same plugin version pair, a short smoke test, and only then removal of unused plugins.

Updating directly on the live site without a backup turns CVE patching into a second incident: a dead checkout or a broken hero. Staging is not an agency luxury. It is part of the process that official WordPress hardening describes at the maintenance layer, not only at the file layer.

#How to turn on automatic plugin updates in WordPress

Automatic plugin updates have been part of core since WordPress 5.5. The core team’s announcement states that plugin and theme auto-updates are disabled by default, so nothing happens until an administrator switches them on per plugin in the Plugins screen. Core works the other way round: according to the WordPress upgrade documentation, existing installations receive minor core releases, which include maintenance and security releases, automatically by default.

The same documentation lists the switches an audit should look for in wp-config.php and in code:

  • define( 'AUTOMATIC_UPDATER_DISABLED', true ); turns off every type of automatic update, core included. On a site with no manual update routine, this line alone explains how plugins fall years behind.
  • WP_AUTO_UPDATE_CORE set to false disables core updates, 'minor' keeps only minor releases, and true allows major ones as well.
  • add_filter( 'auto_update_plugin', '__return_true' ); forces auto-updates for every plugin, and a callback on the same filter can limit them to a list of slugs.

From the command line, wp plugin auto-updates enable (WP-CLI 2.5.0 and later) switches auto-updates on for one plugin, or for all of them with --all.

Auto-updates do not replace the staging route described above. They fit small single-purpose plugins whose releases rarely change front-end output. Page builders, form plugins and anything tied to checkout belong on the staging route, because a jump of several minor versions changes templates. The audit records which plugin sits in which group, so the choice is made on purpose and not left to defaults.

#WAF and virtual-patching limits

A WAF (Cloudflare, Sucuri, host rules) and virtual patching buy time between CVE disclosure and deploying a fix. Sucuri Website Firewall documentation describes blocking known request patterns, not removing vulnerable code from disk. That distinction matters when a site owner has heard “we have a firewall, so we are safe”.

The Patchstack State of WordPress Security in 2026 report indicates that typical hosting-plus-WAF setups stop about 12 percent of known WordPress attacks. The rest bypasses signatures, uses a logged-in session, hits an endpoint outside the rules, or targets a plugin with no rule yet. A WAF does not:

  • remove an abandoned plugin from wp-content/plugins/,
  • fix missing nonce and current_user_can checks in custom code,
  • replace a version cross-check against the CVE database,
  • guarantee protection when the attack goes through a logged-in editor (as with CVE-2023-6449).

A sensible setup is WAF as the first line plus an update routine as the lasting line. A WAF alone without triage and staging leaves exactly the state we found: Elementor and the form unpatched for years because “the firewall should be enough”.

#WordPress plugin security audit checklist

A security audit is methodical, not clever. Its core:

  • Inventory every installed plugin and theme, and cross-check each version against its known CVEs and minimum-safe version. That is what surfaced the Elementor and Contact Form 7 exposure here.
  • Triage found CVEs by severity, privileges and whether the feature is enabled on production.
  • Test custom and theme code for WordPress security basics: nonce and capability checks on actions, input sanitised before the database, output escaped before the page, and endpoints that require authorisation.
  • Check server-level exposure: an open xmlrpc.php, PHP errors on production leaking paths, missing security headers.
  • Review update, staging and backup policy, because an unmaintained site drifts back into this state within months.
  • Assess whether the WAF actually sees the attack paths for the found CVEs, or only gives a sense of coverage.

The version cross-check is the unglamorous part that catches the most, because the most common WordPress vulnerability is not a clever zero-day. It is a popular plugin three versions behind its patch.

#How to list plugin versions for a WordPress audit with WP-CLI

The version inventory does not need a paid scanner to start. On a server with WP-CLI, wp plugin list prints what is installed, which version is available and whether auto-updates are on:

wp plugin list --fields=name,status,version,update_version,auto_update --format=csv

Adding --status=inactive narrows the list to deactivated plugins, the files that still sit on disk although nobody uses them. For plugins from the WordPress.org directory, the optional fields wporg_status and wporg_last_updated show whether the plugin is still listed and when it last shipped a release, the quickest signal of abandonment:

wp plugin list --fields=name,version,wporg_status,wporg_last_updated

The CSV is the input for the CVE cross-check: each name and version pair is compared with the affected version ranges in the vulnerability database the audit uses. A second command, wp plugin verify-checksums --all, compares plugin files with the checksums WordPress.org publishes, which catches files modified by an earlier compromise. It can only verify plugins that WordPress.org has checksums for, so commercial plugins distributed outside the directory need a separate check.

#Security risks of AI-built WordPress sites

This audit was about outdated third-party plugins, a neglect pattern. AI-assisted deploys add a second layer. They install plugins fast, often without setting up an update and staging routine, so versions drift behind patches even faster. AI-generated custom code often ships without the nonce, capability and sanitisation checks WordPress expects, so you inherit both an outdated-plugin problem and a generated-code problem at once.

#How to fix outdated plugin vulnerabilities

Remediation order follows risk, not convenience:

  • Patch or remove plugins with live CVEs immediately, starting with file-upload and injection paths, after a staging test.
  • Remove abandoned or unused plugins, shrinking the surface permanently (files off disk, not only deactivation).
  • Close server-level exposure (xmlrpc.php, production error display, missing headers).
  • Set the WAF as a time buffer, not as a substitute for updates.
  • Put a real update, staging and backup routine in place so the site does not return to five live CVEs within a year.

If you need to move from a CVE list to a remediation plan on a live site, start with a WordPress security audit.

#Glossary

  • CVE - a Common Vulnerabilities and Exposures identifier, a public catalogue entry for a specific known vulnerability.
  • CVE triage - sorting flaws by severity, privileges and feature exposure, not by the order in the updates screen.
  • SQL injection - an attack that smuggles database commands through unsanitised input.
  • Stored cross-site scripting - malicious script saved on the site and served to other users.
  • WAF - a web application firewall that filters HTTP requests; it narrows attack windows, it does not remove vulnerable code.
  • Nonce / capability check - WordPress mechanisms that confirm a request is intentional and the user is allowed to make it.
  • CVSS - Common Vulnerability Scoring System, the FIRST standard for scoring vulnerability severity from 0.0 to 10.0.
  • CNA - CVE Numbering Authority, an organisation authorised by the CVE Program to assign CVE IDs and publish CVE records.

#Conclusion

A WordPress site does not have to be interesting to be attacked. It has to be running a known-vulnerable version of a popular plugin, which describes a large share of sites that have not been audited. The dominant risk is also the most boring to fix: cross-check every plugin version against its CVEs, triage, patch through staging, do not trust a WAF alone, and remove what you no longer use. The site in this audit was one scan away from trouble and did not know it. Most sites in that position do not.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Related cluster

Explore other WordPress services and knowledge base

Strengthen your business with professional technical support in key areas of the WordPress ecosystem.

Frequently asked questions

Practical answers to apply the topic in real execution.

How do outdated plugins become a security risk?#
A plugin with a known vulnerability is safe only while it is patched. Once a CVE is published and you have not updated, the exploit is public and your version is a scanner target. On the site in this audit, Elementor was pinned at 3.11.1 while the last of the relevant fixes shipped in 3.18.2, leaving five published CVEs live, and Contact Form 7 at 5.8 was exposed to CVE-2023-6449 (arbitrary file upload). The fix is risk-based triage, updates on staging, and removing plugins you no longer need.
What is CVE-2023-6449?#
It is an arbitrary file upload vulnerability in Contact Form 7 itself, affecting versions up to and including 5.8.3 and fixed in 5.8.4. Wordfence, acting as CVE Numbering Authority, published it on 1 December 2023. An authenticated user with editor-level rights or higher could upload arbitrary files. According to the NVD description, the plugin's .htaccess configuration prevents code execution in most cases and the uploaded file is deleted immediately by default, but combined with a second flaw such as local file inclusion it can lead to remote code execution. It shows why an old version of a popular plugin is a published, documented risk rather than a theoretical one.
In what order should you triage CVEs on WordPress?#
First flaws with a public exploit and an unauthenticated or low-privilege path (upload, RCE, SQLi). Then authenticated flaws at editor or lower if that role exists on production. Last, XSS that needs an admin and issues only on rarely used endpoints. The CVE number and publish date do not replace CVSS weight or whether the feature is actually exposed on your site.
Is a WAF enough instead of updating plugins?#
No. A WAF and virtual patching buy time between disclosure and deploying a fix. They do not remove vulnerable code from disk and do not cover every attack variant. The Patchstack State of WordPress Security in 2026 report notes that typical hosting-plus-WAF setups stop about 12 percent of known WordPress attacks. The rest needs an update, deactivation or plugin removal.
Why test updates on staging?#
Page builders and forms often break templates, shortcodes and payment integrations when you jump several minor versions. Staging with a database copy and the same plugins shows regressions before production. After the test you push the same versions live or roll back without downtime. Updating directly on production without a backup turns CVE patching into a second incident.

Need an FAQ tailored to your industry and market? We can build one aligned with your business goals.

Let’s discuss

Related Articles