How attackers find your vulnerable WordPress plugins
Most WordPress compromises start with an outdated plugin, not core. How version fingerprinting and user enumeration work, and the hardening that helps.
When a WordPress site is compromised, the entry point is rarely WordPress itself. Core has shipped automatic background updates for minor releases since 2013 (WordPress 3.7), and the security team maintains it closely. The soft underbelly is everything you bolted on top: third-party plugins and themes, written by thousands of different authors at wildly different levels of care.
Core is well maintained. Your plugins are the attack surface.
A typical WordPress install runs somewhere between a handful and a few dozen plugins. Each one is code that executes with the full privileges of your site — it can read your database, write files, and render HTML to visitors. A single plugin with an unauthenticated vulnerability is enough to expose the whole site, no matter how current your core version is.
Two things make this worse than it sounds. First, plugins get abandoned: an author stops shipping updates, a vulnerability is later discovered, and there is no fix — but the plugin stays installed and active on tens of thousands of sites. Second, deactivating a plugin does not remove its code from disk. A deactivated-but-still-present plugin can remain reachable over HTTP, so "we turned it off" is not the same as "we removed it."
How version fingerprinting works
Attackers do not guess. They read your exact plugin and theme versions straight off the public site, then look those versions up against a vulnerability database. WordPress leaks version information in several predictable places.
readme files at known paths
Most plugins ship a readme.txt at a fixed, publicly readable path, and it contains the
version in a Stable tag line:
GET /wp-content/plugins/example-plugin/readme.txt
=== Example Plugin ===
Stable tag: 2.3.1
Combine the plugin folder name (example-plugin) with that version and you have a precise
fingerprint, retrievable with a single unauthenticated request.
Versioned asset URLs
WordPress appends a ?ver= cache-busting query string to the CSS and JavaScript that plugins
and themes enqueue. That version string is usually the plugin or theme version verbatim:
<link rel="stylesheet"
href="/wp-content/plugins/example-plugin/style.css?ver=2.3.1">
<script src="/wp-content/themes/example-theme/main.js?ver=1.4.0"></script>
Anyone viewing your page source sees this. So do automated crawlers, which is the point: a botnet can scan the entire web for one specific vulnerable version and hit only the sites that match.
Why the exact version is the whole game
A vulnerability is fixed in some release — say the flaw exists up to 2.3.1 and is patched in 2.3.2. If your fingerprint reads 2.3.1 or lower, an attacker knows, before touching your login page, that a public exploit applies to you. Version disclosure turns "is this site vulnerable?" into a database lookup. This is a class of exposure rather than any single flaw; new plugin vulnerabilities are published every week, and the ones that matter are the unauthenticated remote issues in widely installed plugins.
How user enumeration works
Knowing a valid username is half of a password attack. WordPress hands out usernames through several endpoints unless you close them.
-
Author archive redirects. Requesting
/?author=1typically redirects to that user's archive URL, and the archive slug is often the account's login name:
Walking the numeric IDs enumerates the accounts one by one.GET /?author=1 → 301 /author/jsmith/ -
The REST API users endpoint. By default
/wp-json/wp/v2/usersreturns a JSON list of authors, and each entry'sslugcommonly matches the login name:[ { "id": 1, "name": "J Smith", "slug": "jsmith" } ] - Login error differences. If the login form (or XML-RPC) responds differently to an unknown username than to a valid username with the wrong password, the difference itself confirms which names exist.
A confirmed list of usernames feeds credential stuffing and password spraying. It is especially dangerous combined with credentials lifted by infostealer malware: valid usernames plus a leaked password list make brute force cheap and quiet.
Hardening
Update, and remove what you don't use
Keep every plugin and theme current — turn on automatic updates where you can. Then delete, do not merely deactivate, anything you are not using; unused code on disk is still attack surface. Prefer plugins that are actively maintained, and periodically check whether anything you rely on has been abandoned.
Make login attacks expensive
Enforce strong, unique passwords, turn on multi-factor authentication for every account that can reach
wp-admin, and rate-limit or lock out repeated failed logins. If you do not use XML-RPC or the
REST API for authenticated actions, restrict or disable them. A WAF or IP allow-list in front of the login
page adds another layer.
Reduce username disclosure
Set author slugs that differ from login names, restrict the unauthenticated REST users endpoint, block
/?author= enumeration, and make sure login responses do not distinguish "no such user" from
"wrong password."
Reduce version disclosure — but treat it as second order
Stripping the generator meta tag, removing ?ver= query strings, and blocking
access to readme.txt raises the effort for automated targeting. Be honest with yourself about
what this buys you, though: hiding a version number does not patch anything, and a determined scanner can
still fingerprint you. Obscurity is a supplement to updating, never a substitute. The same goes for stray
wp-config.php backups and editor swap files left in the web root — the fix is to remove them,
the same way you would treat an exposed .git
directory.
How Cyber Report checks this
An external Cyber Report scan fingerprints WordPress the same way an attacker would — reading plugin and theme versions from public assets, flagging ones that are outdated, and matching detected versions against public vulnerability data to surface known-vulnerable plugins, themes and core. It also tests the author-archive and REST username-enumeration endpoints and looks for exposed configuration backups. Every check is read-only and non-intrusive; nothing is changed or logged into.
See what we scan, or run a scan on your own domain to see where you stand.