Orca Watch // rapid response
Comment2Shell: a blog comment that ends in a root shell_
CVE-2026-93485 · WordPress Core 4.7 → 7.1.0 · unauth stored XSS → RCE · patched in 7.1.1
TL;DR. An anonymous visitor leaves a comment. It carries no visible script. WordPress sanitises it on save, then re-formats it on display, and the payload lives in the gap between those two passes. When an admin opens the page, the comment fires as JavaScript inside wp-admin, rides the admin session to upload a plugin with a webshell, and now someone else runs PHP on your box. No login. No plugin dependency. Core WordPress. Patch to 7.1.1.
I have spent a good chunk of my career telling people that XSS on a marketing blog is a low-priority finding. Reflected pop-up, screenshot for the report, move on. This is the bug that makes that lazy triage bite you. Comment2Shell is stored XSS on the single most boring surface WordPress has, the comment box, and it walks all the way to server-side code execution without a second bug to lean on.
The vector is the wpautop() function in wp-includes/formatting.php. That is the routine that turns double line breaks into paragraph tags so your comments do not render as one wall of text. It has quietly formatted comments for years. It is also where the wheels come off.
The parser disagreement
WordPress does two separate things to a comment, at two separate times, and they do not agree with each other.
- On save: the comment gets run through
wp_ksesstyle sanitisation. It checks the HTML against an allow-list of tags and attributes. A comment that only uses permitted tags passes. Nothing looks dangerous, because at this stage nothing is. - On display:
wpautop()reformats that stored, already-approved HTML to add paragraph structure. This pass runs a set of regular expressions over the markup.
The trick, from Rafie Muhammad's write-up, is a line break placed inside an attribute of an otherwise allowed tag, combined with a stray > character in that attribute value. On save it is a legal-looking allow-listed tag, so it survives sanitisation. On display, wpautop()'s regex misreads where the tag actually ends. It treats the injected > as the tag boundary, splits the element apart, and relocates the attacker's text into a position the browser now parses as a live event handler.
Sanitisation validated one structure. The formatter emitted a different one. The browser executes the second structure. Classic mutation XSS, except the mutation engine here is a core function nobody thinks of as attacker-reachable.
# 1. attacker submits (survives wp_kses on save):
<a title="safe
> onmouseover=alert(1)">hi</a>
# 2. wpautop() reformats on display, mis-parses the tag boundary
# at the injected '>' and re-emits the attribute text as markup:
<a title="safe> onmouseover=alert(1)">hi</a>
# -> the handler is now live in the DOM. zero click on load.Structure is illustrative. Muhammad's post has the exact payload and the regex path. I am not reproducing a working exploit here, the concept is enough to understand why your allow-list did not save you.
XSS is bad. This is worse.
Stored XSS on its own is bounded. It runs in a visitor's browser with a visitor's access, which on a public blog is close to nothing. Comment2Shell reaches the server because of what an administrator's browser is allowed to do once it is inside wp-admin. The full chain:
- Plant. Unauthenticated attacker submits the crafted comment through the normal public form. Same box your readers use. No account, no nonce, no cookie.
- Land. The comment appears on the page. Held for moderation by default, but Muhammad describes paths around that, and a previously-approved commenter gets auto-approval on the next one anyway.
- Fire. A logged-in admin opens the page. The handler executes automatically on load, zero click, in an authenticated
wp-admincontext. - Escalate. The script uses the admin's own session to push a plugin containing a webshell through the standard admin install flow.
- Own. Attacker hits the webshell, runs arbitrary PHP as the web user. Persistence, data access, lateral movement into whatever else that host can reach.
The one human dependency is step three: an admin has to view the page carrying the comment. On any site where staff read or moderate comments, that is not a barrier, it is a scheduled event. In Muhammad's PoC the whole thing is zero-click for the admin. And unlike Click2Shell, the sibling bug fixed in the same release, this one does not need a second vulnerable theme to complete the chain.
"But we moderate our comments." That is not the control you think it is. Patchstack said it plainly: moderation is not a security control. Approval gates spam, it does not gate a payload that only needs to render once in front of the right pair of eyes. If comments render on your site, you are in scope.
Who and what is affected
WordPress Core 4.7 through 7.1.0. That is not a niche version window, that is the overwhelming majority of live installs. The bug triggers anywhere content flows through wpautop(). Block themes, which have been the default since Twenty Twenty-Two, are confirmed affected. Some classic themes too where they format comments through the same pass, and the write-up names Twenty Twenty-One specifically.
CVSS 3.1 vector is AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L, which nets out to 7.1. Note the scope change (S:C) and that user interaction is the only thing keeping this off a 9-point score. The confidentiality/integrity/availability metrics read as Low because they score the XSS primitive itself, not the RCE it bootstraps. Do not let the 7.1 lull you. The realistic impact is full host compromise.
Fix it
WordPress shipped the fix on 17 September 2026. Update core. If you run auto-updates for minor releases you already have it, but verify rather than assume.
7.1.x → 7.1.1 # current line
7.0.x → 7.0.5
6.9.x → 6.9.8
...backports down to 4.7.36 for older maintained branches7.1.1 is a security release that closes 11 issues in total, including path traversal and CSRF flaws, plus the Click2Shell theme-install chain. Comment2Shell was the only one an attacker with no account could reach, which is exactly why it is the one to prioritise.
If you cannot patch this hour
- Close comments site-wide, or turn them off on individual posts. No render, no trigger.
- Force manual approval on every comment and stop auto-approving return commenters. Slows it, does not stop a determined operator, buy yourself the patch window and nothing more.
- WAF or a security plugin ruleset to filter the malformed comment payload. Compensating control, not a fix.
- Audit installed plugins for anything you do not recognise. Patching closes the door, it does not evict anyone already inside. If you have reason to think you were a target, hunt for unfamiliar plugins and files and treat the box as suspect.
Why I care about this one
This is the third time recently that a formatting or sanitisation edge case in WordPress core, not some abandoned plugin, has turned into a real RCE path. wp2shell in July was unauthenticated and landed on CISA's exploited list. A login-page XSS in August chained through an admin the same way this does. The pattern is consistent and it is uncomfortable: the dangerous bugs are not in the obvious injection sinks, they are in the utility functions that transform trusted-looking data on the way to the page.
Two save/display passes that disagree about what a string means is a whole vulnerability class, not a WordPress quirk. Anywhere you sanitise on input and re-serialise on output with a different parser, you have the same shape of problem waiting. Markdown renderers, template engines, HTML mutators in your own app. Go look at yours. The comment box on a brochure site was never supposed to be a path to the shell, and yet here we are.
Sources
- Orca Security Research Pod — Comment2Shell XSS-to-RCE Chain (Orca Watch)
- The Hacker News — Comment2Shell coverage (links Rafie Muhammad's write-up)
- Patchstack — WordPress 7.1.1 advisory & CVE assignment
- Field Effect — WordPress 7.1.1 fixes two paths to shell
- WordPress.org — 7.1.1 maintenance & security release notes
Elusive Thoughts // securityhorror.blogspot.com · posted 24 Sep 2026 · patch, then hunt.
No comments:
Post a Comment