NewTry MagicWP now - first month free
Back to blog

Comment2Shell (CVE-2026-93485): How a WordPress Comment Became Server Code Execution

Comment2Shell (CVE-2026-93485) lets an anonymous comment turn into stored XSS and, through an admin session, server code execution. Fixed in WordPress 7.1.1.

Comment2Shell (CVE-2026-93485): How a WordPress Comment Became Server Code Execution

On September 17, 2026, WordPress shipped 7.1.1 with a fix for a core flaw that a security researcher named Comment2Shell. The short version is unpleasant: an anonymous visitor could leave an ordinary-looking comment that carried no visible script, and later, when the right person opened the page, that comment could run code on your server. No plugin was involved. No login was needed to plant it. The bug lived in WordPress core itself, in the code that has formatted comments for years.

This article walks through what the flaw actually does, which sites are exposed, why "we moderate our comments" is not the protection people assume it is, and exactly what to do about it. If you run a WordPress site that accepts comments, this is the fix to prioritize from the 7.1.1 release, and the reasoning below explains why.

TL;DR

  • Comment2Shell (CVE-2026-93485) is an unauthenticated stored XSS in WordPress core's wpautop() paragraph-formatting function, fixed in WordPress 7.1.1 on September 17, 2026. Patchstack, which assigned the CVE, rated it 7.1 (CVSS 3.1).
  • The payload goes in through the normal comment form as an anonymous visitor. It passes WordPress's comment sanitizer because it looks harmless, then the display filters rearrange it into a live JavaScript event handler.
  • It only forms a working attribute on sites that render comments through a block theme (or a classic theme that uses the same block-based comment rendering, such as Twenty Twenty-One via the_content()).
  • The script runs in the browser of whoever loads the page. If that visitor is a logged-in administrator, the script can upload a plugin containing a web shell, which is the known route from admin-context XSS to remote code execution.
  • Update to WordPress 7.1.1, or to the patched release on your branch (backports go all the way down to 4.7.36). There is no report of exploitation in the wild, and it is not on CISA's known-exploited list, but the fix is public and so is the write-up, so treat it as urgent.

What Comment2Shell actually is

Comment2Shell is the nickname a researcher at Awesome Motive, Rafie Muhammad, gave to CVE-2026-93485. WordPress described it in its own release notes as an unauthenticated stored cross-site scripting issue "via paragraph formatting, subject to comment approval." Patchstack, which handled the CVE assignment, called it a stored XSS in wpautop() and scored it 7.1 on the CVSS 3.1 scale, with the vector AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L.

Two properties make it worth understanding rather than just patching and forgetting.

First, it is unauthenticated at the entry point. The attacker does not need an account, a nonce, or a cookie. They submit a comment through the same form your readers use.

Second, it starts as cross-site scripting but can end as remote code execution. XSS on its own is bad but bounded; it runs in someone's browser. This one reaches the server because of what an administrator's browser is allowed to do inside wp-admin. The name Comment2Shell captures that arc: comment in, shell out.

The word "stored" matters too. This is not a reflected XSS that needs a victim to click a crafted link. The malicious content sits in your database as a saved comment and runs every time the page is rendered for a qualifying visitor, until you remove it.

How a harmless comment turns into a live script

The interesting part of this bug is that the attacker never submits any JavaScript. They submit valid, allowlisted HTML, and WordPress's own formatting steps assemble the attack. Here is the chain at a level that explains the risk without handing anyone a working payload.

WordPress treats comment input in two separate stages, and the gap between them is where the flaw lives.

When a comment is saved, WordPress runs it through KSES, its HTML sanitizer. KSES uses an allowlist. For comments, that list permits a small set of tags and attributes, including <blockquote cite=""> and <code>. A comment built only from those tags looks completely benign to the sanitizer, because nothing in it resembles the script or event-handler markup KSES exists to strip.

When the comment is later displayed, WordPress runs it through a chain of formatting filters on the comment_text hook. One of those is wpautop(), the function that converts line breaks into paragraphs across nearly everything WordPress renders. wpautop() does not parse HTML; it rewrites text with regular expressions. One of those expressions was not aware of quoted attribute values.

The specific failure works like this. wpautop() moves a <blockquote> out of a wrapping <p> using a pattern that captures everything up to the first > character. That is fine for a normal tag, where the first > is the one closing it. It breaks when a > appears inside a quoted attribute value, because the expression then mistakes the middle of an attribute for the end of the tag and moves a <p> element into it.

The attacker does not even have to supply that stray >. A few lines earlier, wpautop() protects newlines inside tags by swapping each one for an HTML-comment placeholder, and an HTML comment ends in -->. So a plain line break placed inside a cite attribute value becomes a > inside that attribute value. WordPress supplies the exact character the attack needs. A newline in an attribute is not something KSES has any reason to remove, so it survives sanitization.

From there the tag gets torn apart, the attribute's real closing quote is stranded, and the remaining filters finish the job. Attacker-controlled text, which started life as ordinary text inside a <code> element, ends up in the position where HTML attributes go. The result is a tag that carries a real event handler plus autofocus, so the handler fires as the page loads, with no click required.

The takeaway for non-developers: the attacker sends WordPress a comment that is 100% allowed HTML. WordPress's paragraph formatter accidentally rearranges it into a script trigger while displaying it. The sanitizer never sees anything wrong because, at the moment it checks, nothing is wrong yet.

Why block themes are the deciding factor

Comment2Shell does not fire on every site. One step in the chain depends on how your theme renders content.

The final piece needs a call to wptexturize() at the right point, which curls a stray quote into a typographic character and, in doing so, helps close off the injected attribute. Block themes call wptexturize() through their template rendering path by default. Some classic themes do too, when they render content through the_content() in a way that routes through the block templates; the Twenty Twenty-One theme is a named example. Sites that render comments a different way are not affected by this particular chain.

That is a real limiting factor, but do not lean on it as a defense. WordPress has shipped block themes as the default since Twenty Twenty-Two, so a large and growing share of sites qualify. If you are on a recent default theme, or any block theme, assume you are in scope. "We use a classic theme" is not a reliable exemption either, because whether your classic theme is affected depends on its internal rendering, which most site owners have never audited.

Site setup Exposed to the XSS chain?
Block theme (Twenty Twenty-Two and later, most modern themes) Yes, by default
Classic theme rendering comments through block templates (e.g. Twenty Twenty-One via the_content()) Yes
Classic theme with a different comment-rendering path Not through this specific chain
Any of the above, already updated to the patched release No, the flaw is fixed

The honest reading of that table: unless you have specifically confirmed your comment-rendering path is not affected, patch and assume you were exposed.

"Subject to comment approval" is not the reassurance it sounds like

WordPress's release note attached the phrase "subject to comment approval" to this issue, and it is easy to read that as "so as long as we moderate comments, we're fine." That reading is wrong, and it is the single most important thing to understand about your own exposure.

For the payload to run, the comment has to appear on the page. But there are several ordinary, common situations where a comment reaches the page with no moderator ever approving it.

Moderation is off by default. WordPress does not hold every comment for review out of the box. The setting that gates a first-time commenter is a separate option, "Comment author must have a previously approved comment," under Settings, Discussion. If someone has had one comment approved before, every later comment from that same name and email is auto-published. On a site with any comment history, that describes a lot of people.

A stock install ships with an already-approved commenter. Every fresh WordPress install includes the sample comment from "A WordPress Commenter." Because WordPress auto-approves a comment when its author name and email match a previously approved comment, an attacker can submit under that exact identity and be published immediately, with no moderator involved. Many live sites never deleted that sample comment.

The "off" toggle can simply be off. Plenty of sites uncheck "Comment author must have a previously approved comment" deliberately, to reduce friction. On those sites a brand-new identity is stored as approved on the first try.

Even a held comment can be viewed. A pending comment still renders for anyone carrying the comment_author_<COOKIEHASH> cookie, which is anyone who has ever commented on the site. WordPress exposes held comments to their author through a moderation-hash preview URL, and that mechanism can be used to get the payload viewed without approval.

Patchstack put it plainly in its write-up: moderation is not a security control. The CVE record, per the researcher, adds that the approval requirement can be bypassed. Approving comments is routine work, the malicious comment looks unremarkable in the moderation queue, and returning commenters skip the queue entirely. Do not treat "we moderate" as a patch.

From cross-site scripting to a shell on the server

The escalation from XSS to code execution is not a second bug. It is what an administrator's browser is already permitted to do, weaponized by the injected script.

The event handler runs in the session of whoever loads the infected page, with that person's access level. For most visitors that is limited. For a logged-in administrator, it is not, because an administrator can install plugins.

The plugin installer accepts a zip upload. It checks the upload_plugins capability, which resolves to install_plugins, a capability administrators hold. So a script running in an admin's session can read the upload form's nonce, build a small zip in memory that contains a single PHP file, and POST it to the plugin-upload endpoint. The plugin does not even need to be activated. Once the file is written to wp-content/plugins/, it is directly reachable over the web, and it will execute whatever commands the attacker sends, running as the web server user.

This "admin XSS to plugin upload to web shell" path is a known WordPress escalation, and it is the reason admin-context XSS in core is treated as severe rather than as a cosmetic annoyance. The comment is the delivery mechanism; the administrator's own session is the privilege.

Practically, that means the worst-case Comment2Shell scenario is a chain of events that requires no social engineering beyond an administrator viewing a page on their own site:

  1. An anonymous visitor posts a crafted comment through the normal form.
  2. The comment is published, through one of the no-approval routes above.
  3. An administrator opens the post while logged in.
  4. The script runs in the admin session and uploads a plugin that is a web shell.
  5. The attacker runs commands on the server.

What to do right now

The fix is to update. Everything else is a stopgap for the minutes or hours before you can.

Update WordPress core to the patched release. For the current branches, the fixed versions are:

Branch Patched version
7.1 7.1.1
7.0 7.0.5
6.9 6.9.8
6.8 6.8.9
Older branches, down to 4.7 The branch's own fixed release, as far back as 4.7.36

WordPress backported the fix to every branch still eligible for security fixes, so even old installs have a patched release available. The full per-branch list lives in the official version documentation. Only the latest version is actively supported, so if you are on an old branch, this is a good moment to plan the jump to current rather than sitting on a backport indefinitely.

If your site supports automatic background updates, a minor security release like 7.1.1 is normally applied for you. Confirm it actually landed rather than assuming, because a failed or disabled auto-update leaves you exposed with no notice.

You can check and apply the update from the dashboard, or over WP-CLI if you manage the site from the command line:

terminal
# Check the installed core version
wp core version

# Update core to the latest release on your branch
wp core update

# Confirm afterward
wp core version --extra

On MagicWP, core security updates are applied automatically with rollback available, so managed sites picked up the patched release without manual work. If you run your own stack, verify the version yourself using the commands above.

Before you can patch, close the door. If you genuinely cannot update immediately, the way to stop the payload from ever being stored or shown is to stop accepting comments:

  • Turn off comments site-wide, or at least close them on posts that accept public input. Remember that changing the site-wide discussion setting affects new posts only; existing posts and comment-enabled custom post types keep their current setting, so you may need to close comments on existing content explicitly.
  • A web application firewall or a security plugin may block the crafted comment before it is stored. Patchstack, for instance, stated its customers were protected for this issue through virtual patching. Treat this as a delay tactic, not a substitute for the update.
terminal
# Close comments on all existing published posts (stopgap only)
wp post list --post_type=post --post_status=publish --field=ID \
  | xargs -I % wp post update % --comment_status=closed

Run that against a staging copy first if you have one, and take a backup before making bulk changes to production. It is reversible, but bulk edits deserve a safety net.

Assume nothing about "we were probably fine." The exploitation preconditions are common, not exotic, so the safe assumption for an unpatched, comment-accepting block-theme site is that it was reachable.

After you patch: check whether anything already happened

Updating fixes the flaw. It does not undo a change an attacker made before you updated. If your site was unpatched and accepting comments, and especially if it is a higher-value or higher-traffic target, do a short check rather than assuming the update was enough.

A few concrete things to look at:

  • Unexpected plugins or plugin files. The escalation path drops a plugin folder that need not be activated, so do not rely only on the active-plugins list. Compare the contents of wp-content/plugins/ against what you know you installed.
  • Recently modified or unfamiliar PHP files, particularly small files in odd locations.
  • New administrator accounts you did not create.
  • The comment table for suspicious stored comments containing blockquote with unusual cite values, which you can remove.
terminal
# List all plugin folders on disk, not just active ones
wp plugin list --fields=name,status,version

# Surface PHP files under wp-content modified in the last 7 days
find wp-content -name '*.php' -mtime -7 -type f

# List administrator accounts
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

If any of that turns up something you cannot explain, treat the site as potentially compromised and work from a known-clean backup rather than trying to clean in place. On managed hosting with daily off-site backups and one-click restore, rolling back to a pre-incident snapshot is the fastest clean path; MagicWP keeps daily backups per site for exactly this kind of situation.

There is no separate manual workaround published by WordPress or the researcher, and none is needed. Updating is the fix.

Where Comment2Shell fits in the 7.1.1 release

Comment2Shell was one of 11 security fixes in WordPress 7.1.1, alongside 17 core bug fixes and a batch of block editor fixes. It stands out because it was the only one an attacker with no account could reach. Most of the others required a logged-in user with some level of access, ranging from Contributor up to Administrator, covering issues like a REST API path traversal, a Contributor-level arbitrary post overwrite, and several disclosure bugs.

The same release also fixed a separate flaw nicknamed Click2Shell, in which a crafted link could make WordPress install and preview a theme from WordPress.org and, chained with a second weakness, reach code execution. Click2Shell needed a logged-in administrator to open the malicious link, so its delivery relied on phishing or an existing XSS on the site. The two are different bugs with a shared theme: the administrator session is the target, and the payoff in both cases is code on the server.

If you take one operating principle from this release, make it this: an administrator browsing their own logged-in site is part of your attack surface. Both of the "shell" bugs in 7.1.1 turn a routine admin action, opening a page or clicking a link, into the pivot. Shorter admin sessions, fewer admin accounts, and a habit of not browsing the public front end of your site while logged in as an administrator all reduce the blast radius of bugs in this class, even ones not yet discovered.

Frequently asked questions

Is Comment2Shell being exploited in the wild?

As of the disclosure, there was no report of active exploitation, and CVE-2026-93485 was not on CISA's known-exploited vulnerabilities list. That is reassuring but time-sensitive: the fix is public, the technical write-up is public, and the preconditions are common. Public details plus a public patch is exactly the window in which opportunistic scanning tends to start, so patch on the assumption that the grace period is short rather than open-ended.

My site moderates all comments. Am I safe?

No, not on that basis alone. Comment moderation is off by default in WordPress, the setting that holds first-time commenters can be bypassed or disabled, a stock install ships with an already-approved sample commenter whose identity can be reused, and even a held comment can be viewed through its moderation-hash URL. Patchstack's framing is the right one: moderation is not a security control. Update to the patched release rather than relying on your moderation workflow.

I use a classic theme, not a block theme. Does that mean I'm not affected?

Possibly, but do not assume it. The attack chain needs a wptexturize() call at a specific point, which block themes make by default and some classic themes also make when rendering content through the_content() (Twenty Twenty-One is a named example). Whether your specific classic theme routes comments through that path is not something most site owners have verified. The safe move is to patch regardless.

What versions of WordPress are affected, and what do I update to?

The flaw affects WordPress core versions from 4.7 through 7.1. Update the current branches to 7.1.1, 7.0.5, 6.9.8, or 6.8.9 respectively; older branches down to 4.7 each received a fixed release, as far back as 4.7.36. The per-branch list is in the official WordPress version documentation. Only the most recent version is actively supported, so moving to current core is better than staying on an old backported branch.

Does patching remove a comment an attacker already planted?

No. Updating fixes the formatting flaw so the payload can no longer form a live event handler, but a comment already stored in your database stays there, and any change an attacker made while you were unpatched is not undone by the update. After patching, check your plugins folder, recently modified files, and administrator accounts, and remove any suspicious stored comments.

How does a comment end up running code on the server if XSS runs in the browser?

The XSS runs in the browser of whoever loads the infected page, with that person's privileges. When that person is a logged-in administrator, the script can use the admin's session to upload a plugin containing a PHP web shell through the standard plugin installer, which administrators are allowed to use. The uploaded file is directly reachable over the web and runs as the web server user, which is how browser-side script becomes server-side code execution. That last step needs an administrator to view the page.

Can a firewall protect me until I can update?

It can help as a stopgap. A web application firewall or security plugin may block the crafted comment before it is stored, and Patchstack said its customers were virtually patched against this issue. Treat that as buying time, not as a fix. The crafted input is valid HTML that only becomes dangerous after WordPress reformats it, so filtering it reliably is harder than filtering an obvious script tag. Update as soon as you can.

What is the difference between Comment2Shell and Click2Shell?

Both were fixed in WordPress 7.1.1 and both can end in server code execution, but they start differently. Comment2Shell begins with an unauthenticated stored XSS delivered through the comment form, and needs an administrator to view the infected page. Click2Shell begins with a crafted link that installs and previews a theme, and needs an administrator to click that link. Comment2Shell is the one an attacker can trigger with no account, which is why it is the priority fix in the release.

Conclusion

Comment2Shell is a reminder that the most dangerous flaws are often the quiet ones: no exotic exploit, no zero-day scramble, just a comment form doing what comment forms do, feeding attacker-controlled text into code that mis-handled it. CVE-2026-93485 turned that into stored XSS, and an administrator's own session turned the XSS into code on the server. The fix is already out, so the work is mostly making sure it actually reached your sites.

The single action that matters is updating to WordPress 7.1.1 or the patched release on your branch. Do that first, confirm it applied, and only then rely on comment settings or a firewall as the backstop they are. If your site was unpatched and taking comments on a block theme, spend ten minutes checking for anything an attacker might have left behind before you close the book on it.

Keeping core patched the moment a security release ships is the whole game here, and it is the part that is easy to let slip when you are running one release behind and everything looks fine. Managed WordPress hosting that applies core security updates automatically, with rollback and daily backups, exists to close exactly the gap Comment2Shell relies on: the window between a patch shipping and a site installing it. However you host, the lesson is the same. Treat the admin session as part of your attack surface, and never let "we moderate our comments" stand in for "we applied the patch."

A
Alex
MagicWP
Writing about WordPress, performance, and the infrastructure that makes sites fast.

Get the best of MagicWP in your inbox.

Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.

Join 12,000+ builders. We send one email a month.