Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

CVE-2026-119977: The WordPress Core RCE That Only Exists During an Update

WordPress 7.1.4 dropped on October 28, 2026 with 22 security fixes. One of them is CVE-2026-119977, an unauthenticated race condition in the core upgrader that reaches PHP code execution. CVSS 3.1 puts it at 8.1 with vec

WordPress 7.1.4 dropped on October 28, 2026 with 22 security fixes. One of them is CVE-2026-119977, an unauthenticated race condition in the core upgrader that reaches PHP code execution. CVSS 3.1 puts it at 8.1 with vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — network reachable, no privileges needed, high attack complexity.

The interesting part is not the score. It is the shape of the bug: the vulnerable state exists only while the site is updating itself, and exploitation is opportunistic polling, not a single crafted request.

The mechanism

During a full-package core upgrade, affected versions (4.7 through 7.1.3) extract the new tree into a predictable directory beneath wp-content/upgrade. That staged copy is a complete WordPress installation, including wp-admin/setup-config.php and the installer flow.

On servers where that tree is reachable over HTTP, an unauthenticated attacker can request the staged installer during the extraction window and drive it. Wordfence's writeup traces the path through class-core-upgrader.php, class-wp-upgrader.php, update-core.php, and setup-config.php; the NVD record links the exact lines in the 7.1.3 tag.

The 8.1 score describes the trigger, not the setup. The preconditions are all of:

  • A full-package core update in progress (plugin or theme updates do not exercise it)
  • HTTP and PHP access to the staging tree — host configuration dependent
  • A predictable or discoverable staging path
  • Enough time before WP_Upgrader cleanup removes the directory
  • Winning the race

PR:N in the vector only means no account is required. AC:H is doing real work here: in lab conditions the staged directory survived for seconds.

The fix in 7.1.4 randomizes the staging directory name, adds an index guard, and blocks unsafe promotion of staged configuration data. Backported to every supported branch through 4.7 — 4.7.39, 5.x per-branch, 6.0.18 through 6.9.11, 7.0.8, 7.1.4. WordPress 4.6 and older received nothing; those installs need a major upgrade, not a point release.

What the telemetry shows

Defiant deployed a Wordfence rule at 2026-10-28 20:30 UTC, roughly three hours after the release. First match: nine minutes later. Over the following 21 hours, more than 18,500 requests targeted staged setup-config.php installer paths. Two of the largest sources spent about 14.5 hours polling small groups of sites rather than spraying broadly — consistent with trying to be present exactly when a target begins its upgrade.

CISA added CVE-2026-119977 to the KEV catalog on 2026-11-03 (catalog version 2026.11.03), classified CWE-367, with a federal due date of 2026-11-06. The entry also carries forensicTriage: Yes — agencies are expected to establish whether the host was compromised before the patch, which is the interesting instruction here: the staging directory is deleted by a successful update, so "was anyone in wp-content/upgrade last Tuesday" is a log question, not a filesystem question. BOD 26-04 binds FCEB agencies; for everyone else the KEV listing is the fastest public confirmation that a flaw is being used.

Also relevant if you run a WAF: the exploit requests a PHP file directly and never bootstraps WordPress, so rule evaluation that happens inside WP does not see it. Wordfence's guidance is to run the firewall with Extended Protection so it executes before the request reaches the file. And the Free tier receives this rule on 2026-11-27 — 30 days after paid tiers. Until then, the patch is the only complete mitigation for those sites, which is exactly what the vendor says.

Verifying your fleet

Version first, per branch — do not compare a 6.8 install against 7.1.4:

# WP-CLI, per site
wp core version --path=/var/www/site
wp core check-update --path=/var/www/site

# Bulk over a list of docroots
while read -r p; do printf '%s: ' "$p"; wp core version --path="$p" 2>/dev/null; done < docroots.txt

Patched targets: 4.7.39, 4.8.34, 4.9.35, 5.0.31 … 6.7.11, 6.8.12, 6.9.11, 7.0.8, 7.1.4.

Then check whether anyone was in the staging path while an update ran. The directory is deleted on completion, so there is no file artifact to inspect afterwards — access logs are the evidence, and they age out before you need them:

# Any HTTP request for the upgrader staging tree is suspicious:
# the updater itself uses the filesystem, not the web server.
zgrep -hE 'wp-content/upgrade/.*(setup-config|install)\.php' /var/log/nginx/*access*.log* \
  | awk '{print $1, $7, $9}' | sort | uniq -c | sort -rn | head -20

If you have no tooling that fetches those paths, treat hits as a finding and pull the full request line and user agent for each. Absence of hits is only meaningful if the retention window covers the update.

Interim hardening while you schedule the patch

Block HTTP access to the staging tree. The upgrader writes through the filesystem, so denying the web path should not interfere with core updates — test it on one non-production site before rolling it out:

# nginx — deny the upgrader staging tree
location ^~ /wp-content/upgrade/ {
    deny all;
    return 403;
}

Apache equivalent, in the site vhost:

<Directory "/var/www/site/wp-content/upgrade">
    Require all denied
</Directory>

Two caveats worth stating plainly. Some managed hosts and some plugin update flows differ from the default path; verify on a staging copy rather than assuming. And a deny rule is an edge control on paths you know about — the randomized directory in 7.1.4 is the version that removes the predictable-name problem entirely.

Detection gaps after the fact

The staging files are cleaned up as part of a successful update, so an intrusion that used this path leaves no directory behind. What you can still check:

  • Access logs for the window during and around any core update since October 28
  • Outbound connections from the web user, and any PHP file in the document root with a modification time that does not match a deploy
  • File integrity baselines, if you keep one — this is the case where they earn their keep

If your site ran a full-package core update in the past week and you have no log retention from that window, that is a gap to write down rather than a conclusion to draw either way. Wordfence has not reported confirmed compromises as of the time of writing; that is not the same as "nobody got hit."

Takeaway

The patch is a point release. The operational lesson is not "patch faster" — it is that an update process is attack surface, and verification is a separate step from automation. Confirm the version per branch, keep access logs long enough to answer "was anyone in wp-content/upgrade last Tuesday", and deny that path at the edge as a second layer.

Full writeup, with the per-branch affected and patched ranges: WordPress 7.1.4 fixed a flaw that only exists during the update.

Sources: WordPress 7.1.4 release note and version documentation, Wordfence/Defiant advisory (Oct 29, 2026), NVD record for CVE-2026-119977, CISA KEV alert (Nov 3, 2026).

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.