Start trial

WordPress 7.1.4 Security Release

WordPress 7.1.4 came out on 28 October 2026 with fixes for what WordPress counts as 22 vulnerabilities across 17 advisories. 7.1.3, three weeks ago, had seven.

PublishedOctober 28, 2026
Dave Jong avatar
Dave Jong
Security Research Lead at Patchstack

WordPress 7.1.4 came out on 28 October 2026 with fixes for what WordPress counts as 22 vulnerabilities across 17 advisories. 7.1.3, three weeks ago, had seven.

Almost everything here needs an account first, usually a Contributor. The exception is a race condition in the upgrade process that ends in remote code execution with no account at all. Backports shipped the same day for every supported branch down to 4.7.39.

Vulnerability PatchedRequirementsReported By
Race condition in the core upgrade process, leading to RCENothing (Unauthenticated), but the attacker has to hit the seconds-long window while an update runs, and wp-content/upgrade/ must be reachable over the webUmut Bayram
Stored XSS via whitespace in block delimitersA Contributor+ account, and an editor or admin opening the post in the block editorRafie Muhammad, Awesome Motive
Stored XSS on the personal data erasure requests screenAn Editor+ account, and an admin opening Tools → Erase Personal DataAnthropic
Unpublished Customizer changesets exposedNothing (Unauthenticated)Antonio Lorensius Silaban
Unescaped usernames on the multisite Network Users screensA username containing markup, stored by something other than core, plus a network admin viewing the list (hardening fix)Anthropic
Notes readable, editable and deletable without post accessA custom role or post type that grants moderate_comments without edit access to the postRamon, WordPress Security Team
Stored XSS in Customizer selective refreshA Contributor+ account, and an admin opening the Customizer on that contentRafie Muhammad, Awesome Motive
Stored XSS via javascript: URLs in Interactivity API bindingsA Contributor+ accountLuis Herranz, and multiple independent reporters
Stored XSS via Interactivity API event handler bindingsA Contributor+ accountJon Surrell, WordPress Security Team
Stored XSS via prototype pollution in Interactivity API contextA Contributor+ accountrekter0
Stored XSS and denial of service in Interactivity API bindingsA Contributor+ accountrekter0, Marcel Karlsson, Jon Surrell, Alex Concha
object elements loading admin pages via Interactivity API bindingsA Contributor+ accountrekter0
Stored XSS in the Interactivity Router via uploaded text filesAn Author+ account (upload capability)Anas cherni
Post content shadowing Script Module data via a matching element IDA Contributor+ accountWeston Ruter
Stored XSS in the Categories block via format string injectionA Contributor+ accountBen Bidner, WordPress Security Team
Unsafe preg_replace() replacements in the Image and Playlist blocksAn Author+ account (hardening fix)Ben Bidner, WordPress Security Team
Stored XSS in Comments Pagination and Post Navigation Link block labelsA Contributor+ accountAlex Concha, WordPress Security Team

What the Code Shows

The Upgrade Process RCE

When WordPress installs or updates anything, it extracts the package into wp-content/upgrade/, which the web server serves like any other directory. Until 7.1.4 the extraction folder took its name from the package, so for anything uploaded through the admin the name was fully predictable. The cleanup routine that runs first deleted everything in that directory, including the index.php that stops the folder being listed.

For the few seconds an update takes, a complete WordPress install sits at a guessable and browsable path. The extracted copy includes wp-admin/setup-config.php, which boots on its own without the site’s configuration. Reaching it in time means running the installer against a database you control, inside someone else’s document root.

7.1.4 appends 16 random characters to the folder name, recreates index.php and stops deleting it. A second change in update-core.php, not mentioned in the release post, adds wp-config.php to the list of files never copied out of the working directory.

The window is tiny. You have to request the right path at the moment an update runs, which is why we scored it AC:H and would not call it practical to exploit. It still needs no account and no interaction, the sink dates back to WordPress 2.8, and the window returns on every update, including unattended ones.

A PHP firewall cannot block it. The extracted copy loads WordPress from its own files, and the official zip ships no mu-plugins directory, so the site’s firewall never runs for that request. Block it at the web server: deny access to wp-content/upgrade/, and check the directory is not listable. Between updates it holds nothing but an index.php.

Interactivity API

Seven of the 17 advisories are Interactivity API bugs with a common root. KSES, which decides what HTML a user without unfiltered_html can save, allows any data-* attribute with any value, and every Interactivity directive is a data-* attribute: data-wp-interactive, data-wp-context, data-wp-bind--href. A Contributor could write directives into a post, have them pass filtering untouched, and the client runtime would act on them when a visitor loaded the page.

From there: bindings that set javascript: URLs, bindings onto on* handlers the PHP side already refused, reserved keys like __proto__ reaching the context merge, bindings on object elements that get past the data and type checks KSES performs, and a CSS parser that reprocessed its input on every iteration. That last one is the denial of service, and it hangs the visitor’s browser tab, not the server. 7.1.4 fixes these in three places: KSES strips bindings that would change a value it validated, the PHP directive processor restricts bindings on object elements, and the runtime refuses the unsafe targets.

The Interactivity client runtime shipped in 6.4, one release before the PHP class in 6.5, which is why WordPress lists 6.4 and 6.5 as affected by the same number of issues.

Script Module Data Shadowing

The Interactivity API reads its state from a script element found with getElementById(), which returns the first element with that ID whatever its tag. WordPress prints the real data block on wp_footer, after post content, so stored content carrying the same ID gets there first and supplies the runtime’s state. 7.1.4 checks that the element is actually a script element.

Customizer

Selective refresh built partial definitions from data-customize-partial-* attributes in the preview document, so post content could declare a partial whose selector went to jQuery’s $(). jQuery treats a string starting with < as HTML to build rather than a selector to match, the same behaviour behind one of the 7.1.3 fixes. 7.1.4 uses .find(), which only reads selectors.

Separately, a Customizer preview request with no changeset UUID adopted the most recent unpublished changeset, whoever wrote it, with no capability check, giving an anonymous visitor the UUID and the rendered preview. Setting values stayed protected by their own capabilities. 7.1.4 requires the customize capability.

Blocks

In three block fixes, content that had already been filtered was reinterpreted afterwards. The Categories block ran its dropdown label through wp_kses_post() and then put the result into a sprintf() format string, so format directives in the label were expanded after filtering finished. The Image and Playlist blocks built markup with preg_replace() using attacker-influenced matched text as the replacement string, so PHP expanded backreferences like $0 inside it; without a replacement limit that could splice one tag into another tag’s attribute. Both now use the HTML API. The Comments Pagination and Post Navigation Link blocks printed their label attribute without escaping it.

Block Delimiter Whitespace

WordPress parses block delimiters twice, in PHP on the server and in JavaScript in the editor, and the two disagreed on what counts as whitespace. PHP’s \s matches six characters; JavaScript’s also matches non-breaking spaces, various Unicode spaces and a byte order mark. A delimiter using one of those is an inert HTML comment to PHP and a real block, with attacker-controlled attributes, to the editor. 7.1.4 pins every parser to the same five characters.

Notes Permissions

The REST comments controller took moderate_comments as enough to edit a note, so a role holding it could read, change and delete notes on posts it could not edit. On a default install this is moot, because administrators and editors hold moderate_comments and edit_others_posts together. It matters where something separates the two: a custom role, a plugin granting the capability, or a post type with its own capability set. 7.1.4 requires edit access to the post the note belongs to.

Timeline

28 October 2026WordPress 7.1.4 released, with backports shipped the same day to every supported branch down to 4.7.39.
28 October 2026Vulnerabilities added to the Patchstack Database.

Update Now

WordPress 7.1.4 is in the Dashboard under Updates or on WordPress.org, and sites with background updates will take it on their own. Backports are already available: 7.0.8, 6.9.11, 6.8.12, 6.7.11, 6.6.11, 6.5.14, 6.4.14 and down to 4.7.39. Sites on 4.6 or older get nothing.

Two things beyond updating. Check that wp-content/upgrade/ is not browsable, which closes the only issue here that needs no account. Then check who holds Contributor and above, since that is where all but two of these start.

Like it? Share it.

Related articles