CVE Watch

WordPress 7.1.3: Seven Core Security Fixes, Explained

· 7 min read
wordpress-core-7-1-3-security-patches

If your site isn’t on WordPress 7.1.3 yet, update it. The October 6 release fixes seven security issues in core, from a stored XSS that fires when a moderator opens the Comments screen to a SQL injection hidden in the export tool. None are known to be under attack, but this is the third security release on the 7.1 branch in about three weeks, and the last one, CVE-2026-87902, was being probed within hours of its patch.

At a glance

ReleaseWordPress 7.1.3, a maintenance and security release (October 6, 2026)
Fixes7 security fixes, 4 bug fixes
CVEsNone assigned in the release notes, and no severity scores published
AffectedEverything before 7.1.3. Some issues are much older: the export SQL injection dates back to 6.5.0
Fixed7.1.3. Backports to older branches (WordPress says “through 4.7”) are still in progress
StatusNo exploitation reported

The seven fixes, and what an attacker needs for each:

#IssueWhat it takes
1Stored XSS on the Comments admin page, via pending commentsAn anonymous comment, plus a moderator clicking a link in it
2Comments on private and unpublished posts disclosedNothing. Any visitor
3Second-order SQL injection in the WXR exportA malicious value already in the database, plus an administrator running a single-type export
4XSS in Imgur embedsA Contributor account, plus someone viewing the content
5Denial of service in WP_Http::make_absolute_url()A Contributor account
6Authors can make posts stickyAn Author account
7{status}_{type} hook name collisionA plugin that passes an unchecked status or post type to wp_insert_post()

What’s wrong

Most of these are small mistakes in code that’s been around for years. Three are worth walking through.

The Comments screen XSS

The admin Help tab has a click handler in wp-admin/js/common.js that took a link’s href and passed it to jQuery’s $(). Given the right string, $() doesn’t look something up. It builds HTML from it. Since 7.1.0, visitor comments can carry a class attribute, and that’s the missing piece:

  1. Leave a comment. An anonymous visitor posts a comment with a link whose href is an HTML payload, inside an element with the contextual-help-tabs class.
  2. Wait for moderation. The comment sits in the pending queue, which is exactly where an editor or admin is going to look at it.
  3. The moderator clicks. WordPress’s own handler hands the href to $(), and the script runs in the moderator’s logged-in session.

From an administrator’s session, a script can create users or edit theme and plugin files. The pending-comment route affects 7.1.0 through 7.1.2. The fix swaps $() for .find(), which only ever looks things up.

The export SQL injection

This one is “second-order”: the dangerous value goes into the database first and gets used later. When you export a single content type, the WXR exporter gathers the featured-image IDs for those posts from their _thumbnail_id meta, and pastes them straight into a SQL query without checking they’re numbers.

  1. Plant the value. Someone gets a non-numeric _thumbnail_id into the database, through a plugin bug, an import or any other write.
  2. Wait for an export. An administrator exports one content type, say just posts.
  3. The query runs. The planted value becomes part of the SQL query, running with the database permissions of the whole site.

The default “All content” export never reaches this code. The bug has been there since 6.5.0. The fix wraps the IDs in absint() in three places.

Private comments in public feeds

For a single post’s comment feed, WP_Query loaded the comments before checking whether the visitor was allowed to see the post. The check then hid the post but not the comments, and because feeds don’t return a 404, the comments went out anyway. Any visitor could read comments on private, draft and scheduled posts. The fix moves the comment query after the visibility check.

The rest

  • Imgur embeds were on WordPress’s trusted oEmbed list, so their HTML skipped the sandbox other providers get, and Imgur responses can include text the uploader controls. Imgur is now untrusted.
  • make_absolute_url() had a loop that never ended on a relative path like a//../b. A Contributor could trigger it through the block editor’s link preview and tie up the PHP worker.
  • Sticky posts: the REST API only refused a sticky request when a user lacked both edit_others_posts and publish_posts. Authors have publish_posts, so they got through. One && became ||.
  • The hook collision: status changes fire hooks named {new_status}_{post_type}. Save a post with the status delete and type post, and you fire delete_post, a completely different core hook. These hooks now only fire for registered statuses and types.

What to look for

There are no attacks on record to show, so there’s no traffic or IP addresses to list. If you want to check whether anyone tried, these are the traces each issue would leave:

  • Pending comments containing class="contextual-help-tabs" or links whose href contains HTML. Don’t open them from the Comments screen on an unpatched site. Update first.
  • Non-numeric featured-image IDs. This query lists any _thumbnail_id that isn’t a plain number. On a clean site it returns nothing:
SELECT post_id, meta_value
FROM wp_postmeta
WHERE meta_key = '_thumbnail_id'
  AND meta_value NOT REGEXP '^[0-9]+$';
  • Imgur embeds added by Contributors or anyone you don’t fully trust.
  • Sticky posts you didn’t make sticky.

Use your own table prefix if it isn’t wp_.

What to do

1. Update

Update to 7.1.3 from Dashboard → Updates. Sites with automatic minor updates should already have it. Check the version at the bottom of any admin screen to be sure. On an older branch, WordPress is backporting the fixes, but they’re shipping as they become ready, so look for the newest point release of your branch on the release archive or, better, move to 7.1.

2. Clear the oEmbed cache

The Imgur fix only applies to new embeds. WordPress caches oEmbed HTML in _oembed_* post meta and oembed_cache posts, and the update doesn’t touch them, so a malicious embed that’s already stored keeps rendering. Clearing the cache makes WordPress fetch every embed again under the new rules. With WP-CLI:

wp embed cache clear <post-id>

That works one post at a time. On a site with lots of embeds, deleting the _oembed_% post meta and the oembed_cache posts directly in the database does the same in one go. Take a backup first. Then clear your page cache too.

3. Review who can write

Four of the seven fixes need an account: Contributor, Author or Administrator. Look at your Users screen and ask whether every account above Subscriber still needs its role. Old guest-author accounts and departed staff are exactly the footholds these bugs assume.

4. Let core security releases install themselves

On this site we’ve argued for keeping plugin auto-updates off production and pushing them through staging. Core minor releases are the exception. They come from WordPress.org’s release process and security team, they change very little, and the gap between a core patch and the first attacks can be a matter of hours, as it was with 7.1.2. Make sure nothing in wp-config.php has switched them off:

// Allow automatic core security and maintenance releases.
define( 'WP_AUTO_UPDATE_CORE', 'minor' );

// And make sure this isn't set anywhere:
// define( 'AUTOMATIC_UPDATER_DISABLED', true );

'minor' is WordPress’s default, so setting it is mainly about making the decision visible. The real danger is a forgotten AUTOMATIC_UPDATER_DISABLED or a “disable updates” plugin left over from years ago.

The bigger lesson

None of these seven bugs is exotic. Trusting a value because of where it came from (a stored meta field, a trusted embed provider, a link in the admin) and checking permissions in the wrong order are the same mistakes that fill plugin advisories every week. The difference is that core gets audited by a security team and a crowd of outside researchers (Anthropic alone reported three of these seven), and the fixes reach every site with automatic updates on, without anyone lifting a finger.

That’s the case for keeping core current and keeping everything else small. Core is code that a lot of people are watching. Every plugin is code that far fewer people are watching, and each one has to earn its place.

Sources