CVE-2026-5075

All in One SEO <= 4.9.7 - Authenticated (Contributor+) Sensitive Information Exposure via 'internalOptions' Localized Script Data

mediumExposure of Sensitive Information to an Unauthorized Actor
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
4.9.7.1
Patched in
1d
Time to patch

Description

The All in One SEO plugin for WordPress is vulnerable to Sensitive Information Exposure via 'internalOptions' localized script data in versions up to, and including, 4.9.7 due to sensitive internal option data being passed to wp_localize_script() in post editor contexts without effective masking for low-privilege users. This makes it possible for authenticated attackers, with contributor-level access and above, to view configured API/OAuth tokens and license-related values from page source.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.9.7
PublishedMay 19, 2026
Last updatedMay 20, 2026
Affected pluginall-in-one-seo-pack

What Changed in the Fix

Changes introduced in v4.9.7.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-5075 (All in One SEO) ## 1. Vulnerability Summary The **All in One SEO (AIOSEO)** plugin (versions <= 4.9.7) contains a sensitive information exposure vulnerability. The plugin localizes a large set of configuration data into a JavaScript object (typically na…

Show full research plan

Vulnerability Research Plan: CVE-2026-5075 (All in One SEO)

1. Vulnerability Summary

The All in One SEO (AIOSEO) plugin (versions <= 4.9.7) contains a sensitive information exposure vulnerability. The plugin localizes a large set of configuration data into a JavaScript object (typically named aioseo) to support its Vue-based administrative interface. In post editor contexts, the internalOptions data is passed to wp_localize_script() without adequate filtering or masking.

This allows users with the Contributor role (who can access the post editor but should not have access to global plugin settings) to view sensitive data such as AI access tokens, license information, and internal API keys by inspecting the page source or the browser's global JavaScript state.

2. Attack Vector Analysis

  • Endpoint: Post Editor (/wp-admin/post-new.php or /wp-admin/post.php?post={ID}&action=edit).
  • Required Privilege: Authenticated user with at least Contributor level access (permissions to edit_posts).
  • Precondition: The plugin must be active. Sensitive values (like an AI access token or license key) must be set in the database for the exposure to be meaningful.
  • Vulnerable Data Sink: wp_localize_script() outputting the internalOptions object into the HTML of the post editor.

3. Code Flow

  1. Script Registration: When a user enters the post editor, WordPress fires the enqueue_block_editor_assets (for Gutenberg) or admin_enqueue_scripts hook.
  2. AIOSEO Asset Loading: The plugin's asset manager (likely AIOSEO\Plugin\Common\Utils\Assets, invoked via aioseo()->core->assets->load()) prepares scripts for the editor.
  3. Data Localization: Before enqueuing, the plugin calls wp_localize_script(). It gathers data from various option managers:
    • aioseo()->internalOptions (Instance of AIOSEO\Plugin\Lite\Options\InternalOptions).
    • aioseo()->internalNetworkOptions.
  4. Information Leakage: The internalOptions object, which contains sensitive internal state and potentially mirrored sensitive credentials (like aiAccessToken or license details), is dumped entirely into a JSON object assigned to a global JS variable (verbatim aioseo).
  5. Unauthorized Access: A Contributor-level user views the editor. The server sends the localized data. The user executes window.aioseo.internalOptions in the console or views the HTML source to see the secrets.

4. Nonce Acquisition Strategy

This is an Information Exposure vulnerability. No nonce is required to trigger the exposure because the data is automatically localized and sent by the server upon a standard GET request to the post editor page. The attacker only needs a valid session cookie for a Contributor-level user.

5. Exploitation Strategy

The goal is to demonstrate that a Contributor can access data they shouldn't see.

Step-by-Step Plan:

  1. Authentication: Log in to the WordPress site as a user with the Contributor role.
  2. Navigation: Navigate to the "New Post" page (/wp-admin/post-new.php). This page triggers the loading of the AIOSEO sidebar and its associated localized scripts.
  3. Data Extraction: Use the browser_eval tool to extract the contents of the aioseo.internalOptions object.
  4. Payload Analysis: Search the extracted JSON for sensitive keys. Based on app/Common/Ai/Ai.php, interesting keys include:
    • internal.ai.isTrialAccessToken
    • internal.ai.isManuallyConnected
    • Tokens stored within the internal options structure.
    • License keys (often in internal.license).

HTTP Request (via http_request / Playwright):

GET /wp-admin/post-new.php HTTP/1.1
Host: localhost
Cookie: [Contributor Session Cookies]

Extraction Script:

// Run via browser_eval
JSON.stringify(window.aioseo?.internalOptions || "Not Found");

6. Test Data Setup

To confirm exposure, we must first populate the internal options with "secret" data via WP-CLI:

  1. Set a fake AI Access Token:
    wp eval "aioseo()->sensitiveOptions->set('aiAccessToken', 'POC_SECRET_TOKEN_999');"
    
  2. Ensure internal options reflect a connected state:
    wp eval "aioseo()->internalOptions->internal->ai->isManuallyConnected = true;"
    
  3. Create a Contributor user:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    

7. Expected Results

A successful exploit will return a JSON object containing the internalOptions structure.

  • Success Criteria: The output of window.aioseo.internalOptions contains values that are not masked (e.g., the aiAccessToken or specific internal configurations that should be restricted to Admins).
  • Format: A deeply nested JSON object.

8. Verification Steps

  1. Verify via CLI: Confirm the value exists in the database.
    wp option get aioseo_options_internal
    
  2. Compare Access:
    • Check if the same data is visible to the Contributor in the UI (it shouldn't be, as they lack "General Settings" access).
    • If window.aioseo.internalOptions in the post editor contains the same "secret" strings found in the database, the exposure is confirmed.

9. Alternative Approaches

If internalOptions does not contain the specific token, check the broader aioseo object:

  1. browser_eval("window.aioseo") - This will dump the entire localized state.
  2. Search for other sensitive keys: license, apiKey, secret, accessToken.
  3. Check if the exposure occurs on the "All Posts" list (/wp-admin/edit.php) as well, as AIOSEO often enqueues scripts there for quick-edit features.

Check if your site is affected.

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