WordPress 7.1.1 shipped on September 17, 2026 carrying eleven security fixes, seventeen core bug fixes and nineteen fixes for the block editor. One of those eleven arrived with a name, a research writeup and, within days, public exploit code. It is called Click2Shell.
The interesting part is not that it ends in remote code execution. Plenty of chains do. The interesting part is that the core half of it contains no missing escape, no forgotten permission check and no line that looks wrong in review. It is two pieces of correct code reading the same string differently.
We covered the wp2shell flaw in July and WordPress 7.0.3 in August. This is the third core security release we have written about in three months. It is the first one where the bug itself is worth reading as an engineering lesson rather than a patch note.
What WordPress 7.1.1 actually fixes
The release announcement describes WordPress 7.1.1 as a security and maintenance release with "17 bug fixes on Core, 19 bug fixes for the Block Editor, and 11 security fixes." It carries the project’s standard security-release language: "Because this is a security release, it is recommended that you update your sites immediately."
The fix drawing the coverage appears in the version documentation as an issue allowing a crafted URL to install and preview a theme from WordPress.org. It is credited to Paulos Yibelo and pwn.ai, the same research outfit behind the XSS2Shell finding that drove 7.0.3 in August.
Worth noting for anyone quoting a single source: The Hacker News credits pwn.ai alone. The official credits name Yibelo as well.
No CVE has been assigned. pwn.ai’s writeup marks it pending, and The Hacker News reports that WordPress plans to add one. If you track vulnerabilities in a management system keyed on CVE identifiers, this one has to be tracked by version number for now, which is exactly the kind of gap that lets a release slip through a compliance process that only watches CVE feeds.
Fixes were backported through 4.7, the oldest branch still eligible for security updates. Anything on 4.6 or earlier receives nothing and is not going to.
For context on where this sits in the release train: 7.1 "Mary Lou" arrived on August 19, 7.0.4 on August 12, and a 7.2 release squad was announced on September 9. 7.1.1 is the current release, and nothing has shipped since.
The core bug was a disagreement about one string
Here is the mechanism, and it is worth slowing down for.
The WordPress.org Themes API takes a theme slug as a parameter. Server-side, it canonicalizes that value. Whatever arrives, the API resolves it against the catalog and works with the clean version. That is correct behavior, and it is the reason the server-side half of this is not exploitable on its own.
The admin screen’s JavaScript does something else. It keeps the raw request value, the one the browser received, and interpolates it straight into a jQuery selector:
$( 'div[data-slug="' + slug + '"]' ).trigger( 'click' );
So the server sees a valid slug. The browser sees the attacker’s original string. Send something shaped like twentytwenty"]>*>*>*/* and the server resolves it to the twentytwenty theme and is satisfied, while the browser reads it as a selector that closes the attribute match early and then matches a far wider set of elements, and fires a click event on all of them. One of those elements is the Install button.
Nobody skipped validation. The sanitization happened. It happened to a copy of the string that was never handed forward.
That is the whole lesson, and it generalizes well past WordPress. Canonicalization on the server is not sanitization for the client unless the canonical value is what the client actually receives. Any architecture where a server normalizes input and a browser is separately handed the original has this shape available to it, and code review does not catch it easily because each half is defensible in isolation. The server-side developer normalized. The client-side developer used a value that had been normalized. Both statements are true and the system is still broken.
The chain needed a theme, not a plugin
Selector injection that clicks an Install button is a nuisance on its own. Getting from there to code execution takes a second mistake, and that one belongs to somebody else.
The target is Mobile Repair Zone 2.5.4, a real theme in the WordPress.org catalog. Its code registers an AJAX action, wp_ajax_mobile_repair_zone_install_and_activate_plugin, and the callback verifies no nonce and checks no capability. It accepts a plugin_details parameter carrying plugin_url, plugin_text_domain and plugin_main_file, then installs and activates whatever that URL points at.
The detail that makes the chain work is that Mobile Repair Zone is a theme rather than a plugin. An installed but inactive plugin does nothing at all. An installed but inactive theme is different: loading admin-ajax.php with wp_customize=on and customize_theme=mobile-repair-zone runs that inactive theme’s code for the Customizer preview. The theme never has to be activated. Install it, preview it, and its unprotected handler is live.
So the full sequence reads: a crafted link causes a specific catalog theme to be installed and previewed, the preview executes that theme’s code, and the theme’s unauthenticated handler installs an attacker-controlled plugin from an attacker-controlled URL. That is arbitrary PHP running on the server.
Two things are worth taking from this rather than the headline.
First, an unprotected wp_ajax_ handler is a ten-year-old class of bug. Nothing about it is novel. What is novel is what reached it, which was core code the theme author had no relationship with.
Second, "installed but not active" is not the isolation boundary most operators assume it is. Every theme sitting in wp-content/themes that nobody has activated since 2023 is still reachable through the Customizer preview, and its code will run there.
The official catalog is part of the attack surface
This deserves its own paragraph because it changes who owns the risk.
The chain does not work against an arbitrary site with an arbitrary theme. It works because the attacker could name a theme that was, at the time of research, installable from the official WordPress.org directory with one click and carrying an unprotected handler. Core’s one-click install of catalog themes is a trusted path by design. The review standard applied to that catalog is therefore load-bearing for core security, not just for the quality of the themes in it.
That is not an argument against the directory, which is one of the better things about the WordPress ecosystem. It is an argument for treating "available in the official catalog" as a statement about distribution rather than a statement about code quality, which is how a good many agencies quietly treat it.
We have not verified whether Mobile Repair Zone has since been updated or removed from the directory, and neither the release announcement nor the research writeup says. If your workflow involves installing catalog themes on client sites for evaluation, that is worth checking yourself rather than taking on faith.
The scores disagree, and so does the framing
The core primitive is rated High at CVSS 3.1 7.1. pwn.ai and The Hacker News agree on that number.
The full chain is where sources part company. pwn.ai rates it Critical at 9.3, explicitly noting that user interaction lowers the impact because a person has to act. The Hacker News reports 9.6. WordPress has not published a final public severity rating for the chain. If you are handing a number to a client, name the source with it, and prefer the researcher’s own figure over the press figure.
The framing deserves the same care. The word "pre-auth" appears in pwn.ai’s title and in the names of the public proof-of-concept repositories, and it is doing more work than it should. The unauthenticated party is the one who sends the link. The party who triggers the chain is a logged-in administrator who opens it. We made the same point about what actually shipped in 7.1 and about 7.0.3 in August, and it holds again: the entry point is unauthenticated, the payoff is not.
There is no sign the flaw has been used in real attacks.
Public proof of concept changes the clock
That last sentence is accurate, and it is weaker than it sounds.
Working exploit code for Click2Shell is public. Several proof-of-concept repositories exist on GitHub, and a detection template has been submitted to the Nuclei templates project. We have not run any of it and are not linking to it, but its existence is the operational fact that matters more than the severity score.
"No exploitation in the wild" describes what has been observed. It is a statement about the past. Public exploit code is a statement about how much work the next attacker has to do, which is now close to none. For anyone running a portfolio of client sites, the distance between those two sentences is the window you are patching inside.
There is a second clock worth naming, and it is the one that catches agencies rather than end users. On git-based managed hosting, a core update is not a button in the dashboard. It is a commit to a repository, a build, and a deploy to each environment, usually gated by whatever review process the client relationship requires. That is a good practice and it is also a delay, and the delay is measured in business days rather than minutes. A site on a platform with one-click automatic minor updates was patched on September 17. A site whose core version is pinned in composer.json and deployed through a pipeline was patched whenever somebody got to it. If you run the second kind, the honest question is not whether you intend to patch but how many of your environments are still on 7.1 right now, and the answer is usually worse than the estimate.
The counterweight is real and should be said plainly. This still needs an administrator to click a link. A site with few admin accounts, trained staff and a habit of ignoring unsolicited messages about its own dashboard is meaningfully harder to hit than one with fourteen administrators and a shared login. That is not a reason to defer the patch. It is a reason not to wake anybody up at two in the morning to apply it.
The other ten fixes
Agencies patch releases, not individual bugs, and a few of the remaining ten are worth knowing about:
- An unauthenticated stored cross-site scripting issue via paragraph formatting in comments, reported by Rafie Muhammad of Awesome Motive. Unauthenticated and stored is the combination that should get your attention on this list.
- An authenticated path traversal in the REST API templates controller.
- An arbitrary post overwrite reachable by a user with the Contributor role.
- A multisite flaw allowing a single site administrator to network-activate a plugin marked network-only.
- An XML-RPC path that bypasses the capability check on custom CSS.
Two of the eleven fixes were reported by Anthropic. That is a mildly interesting data point about who is doing WordPress security research in 2026 and not much more than that.
The block editor fixes and the seventeen core bug fixes are ordinary maintenance. They are not the reason to schedule the update, but they are a reason not to cherry-pick a single patch and skip the rest of the release.
What to do this week
Update core. That is the whole remediation, and no configuration change substitutes for it.
A few specifics are worth acting on beyond the update itself.
Check what you are actually running. The meta generator tag in page source names the version, and wp core version does the same over WP-CLI. Sites on managed hosting with automatic minor updates enabled will have taken 7.1.1 already. Sites with updates pinned, disabled, or managed through a deployment pipeline will not have. Check rather than assume, and check the environments that are not production, because a staging site with an administrator session is a valid target for this chain.
Treat DISALLOW_FILE_MODS as a mitigation, not a fix. Patchstack notes that with theme and plugin installation disabled, the injection still occurs but cannot end in new code being installed. That is a genuine reduction in impact and a sensible default for production sites regardless of this bug. It does not close the underlying issue, and it does nothing for the cross-site scripting half.
Audit your own AJAX handlers. If you maintain a custom theme that registers wp_ajax_ actions, this is a good week to confirm every one of them calls check_ajax_referer() and current_user_can() before doing anything. The Mobile Repair Zone handler is not exotic. It is what a great deal of theme code looks like when nobody has read it in a while.
Delete themes you are not using. Including the default themes you replaced three redesigns ago. An inactive theme is not inert.
The patch is small, the chain is real, the exploit code is public, and the trigger still requires a human being to click something. Apply it today, verify it everywhere including the environments nobody remembers, and go back to what you were doing.
Frequently Asked Questions
What is Click2Shell?
It is the researcher’s name for a chain that starts with a crafted URL and ends in remote code execution on a WordPress site. The core half is a selector injection in the admin theme-install screen, patched in WordPress 7.1.1. The second half depends on a third-party theme with an unprotected AJAX handler.
Does Click2Shell have a CVE?
Not as of September 21, 2026. pwn.ai lists it as pending and The Hacker News reports that WordPress plans to assign one. Track it by version number until then.
What exactly was the core bug?
The WordPress.org Themes API canonicalized a theme slug on the server while the admin screen’s JavaScript kept the raw request value and interpolated it into a jQuery selector. A crafted value was a valid slug to the server and a selector injection in the browser, which let an attacker fire a click on the Install button.
Which WordPress versions are affected?
Every version through 7.1. Fixes were backported through 4.7, which is the oldest branch still receiving security updates. Sites on 4.6 or earlier are unsupported and will not receive a fix.
How many security fixes are in WordPress 7.1.1?
Eleven, alongside seventeen core bug fixes and nineteen block editor bug fixes, per the official release announcement.
Has it been exploited in the wild?
There is no published report of exploitation. Public proof-of-concept code does exist on GitHub, and a Nuclei detection template has been submitted, so the technical barrier for an opportunistic attacker is now low. Those are two different facts and both are worth stating.
Is this really a pre-authentication flaw?
The entry point is unauthenticated in the sense that anyone can send the link. The chain does not fire until a logged-in administrator opens it. Describing it as pre-auth remote code execution without that qualification overstates the practical risk.
Does `DISALLOW_FILE_MODS` protect me?
Partially. With theme and plugin installation disabled, the selector injection still happens but cannot result in new code being installed, which removes the code-execution outcome. It is a useful production hardening default and it is not a substitute for updating.
Why did it matter that Mobile Repair Zone is a theme rather than a plugin?
Because an inactive theme’s code still executes in the Customizer preview, reachable through `admin-ajax.php`. The attacker never had to activate the theme, only install and preview it. An inactive plugin would not have run at all.
What should I check in my own code?
Every `wp_ajax_` action your themes and plugins register. Each callback should verify a nonce with `check_ajax_referer()` and check the caller’s capability with `current_user_can()` before taking any action. The failure in this chain was the absence of both.
Is WordPress 7.2 out?
No. A 7.2 release squad was announced on September 9, 2026, which means it is in planning. 7.1.1 is the current release.