CVE-2025-14980

BetterDocs <= 4.3.3 - Authenticated (Contributor+) Sensitive Information Exposure

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

Description

The BetterDocs plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 4.3.3 via the scripts() function. This makes it possible for authenticated attackers, with contributor-level access and above, to extract sensitive data including the OpenAI API key stored in plugin settings.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.3.3
PublishedJanuary 8, 2026
Last updatedJanuary 9, 2026
Affected pluginbetterdocs

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2025-14980 - BetterDocs Sensitive Information Exposure ## 1. Vulnerability Summary The BetterDocs plugin for WordPress (versions <= 4.3.3) is vulnerable to sensitive information exposure. The vulnerability exists in the `scripts()` function, which is responsible f…

Show full research plan

Vulnerability Research Plan: CVE-2025-14980 - BetterDocs Sensitive Information Exposure

1. Vulnerability Summary

The BetterDocs plugin for WordPress (versions <= 4.3.3) is vulnerable to sensitive information exposure. The vulnerability exists in the scripts() function, which is responsible for enqueuing and localizing JavaScript files. This function inappropriately includes sensitive plugin settings, specifically the OpenAI API key, in the localized data passed to the browser via wp_localize_script(). Because these scripts are enqueued on admin pages accessible to users with the Contributor role (such as the post editor), an authenticated attacker can extract these secrets.

2. Attack Vector Analysis

  • Endpoint: wp-admin/post-new.php or wp-admin/edit.php (any page where BetterDocs scripts are enqueued).
  • Authentication: Contributor-level access or higher.
  • Vulnerable Function: scripts() (likely within an admin or editor-related class).
  • Vector: Information leakage via wp_localize_script() output in the HTML source.
  • Precondition: The site administrator must have configured an OpenAI API key within the BetterDocs settings (Knowledge Base > Settings > AI-powered features).

3. Code Flow (Inferred)

  1. Hook Registration: The plugin registers the scripts() method to the admin_enqueue_scripts hook.
  2. Function Execution: When a user (e.g., a Contributor) loads the Post Editor, admin_enqueue_scripts fires.
  3. Data Fetching: Inside scripts(), the plugin retrieves settings using get_option('betterdocs_settings') or a similar option key.
  4. Insecure Localization: The function calls wp_localize_script( $handle, $object_name, $l10n_array ). The $l10n_array is populated with settings that include the openai_api_key.
  5. Output: WordPress prints the data as a JSON object in a <script> tag in the HTML head/footer.
  6. Exposure: The Contributor can view the page source or use the browser console to read the object.

4. Nonce Acquisition Strategy

This vulnerability is a Passive Information Exposure. It does not require a nonce to trigger; the server proactively sends the sensitive data to any authenticated user who can access the admin pages where the script is enqueued.

5. Exploitation Strategy

Step 1: Locate the Vulnerable Script Handle and Variable

Use grep to identify the exact variable name used for localization:

grep -rn "wp_localize_script" /var/www/html/wp-content/plugins/betterdocs/ --include="*.php"

Look for calls within a function named scripts. The target will likely be an object name like betterdocs_admin_params or betterdocs_settings.

Step 2: Test Data Setup

  1. Set OpenAI Key: Use WP-CLI to simulate an admin setting up the plugin.
    # Setting the option directly in the database to mimic plugin settings
    # The actual option name might be betterdocs_settings (verify via grep)
    wp option patch insert betterdocs_settings openai_api_key "sk-proj-VULNERABLE-OPENAI-KEY-12345" --format=json
    
  2. Create Contributor:
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    

Step 3: Execution Plan

  1. Login: Log in as the attacker user using the http_request or browser_navigate tool.
  2. Navigate to Editor: Navigate to wp-admin/post-new.php.
  3. Extract Data:
    • Method A (HTML Parsing): Fetch the page HTML using http_request and search for the localized variable.
    • Method B (Browser Evaluation): Use browser_eval to extract the key directly from the JavaScript context.
    // Example: If localized variable is betterdocs_admin_params
    window.betterdocs_admin_params?.openai_api_key || window.betterdocs_settings?.openai_api_key
    

Step 4: Expected HTTP Request (for Method A)

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

6. Test Data Setup Requirements

  • Plugin: BetterDocs <= 4.3.3 must be active.
  • Configuration: openai_api_key must be present in the plugin's settings array (found in wp_options).
  • User Role: A user with the contributor role must be created and authenticated.

7. Expected Results

  • The HTTP response for wp-admin/post-new.php will contain a <script> block containing the OpenAI API key.
  • Example output in HTML:
    <script id="betterdocs-admin-js-extra">
    var betterdocs_admin_params = {"ajax_url":"...","openai_api_key":"sk-proj-VULNERABLE-OPENAI-KEY-12345", ...};
    </script>
    

8. Verification Steps

  1. Identify Localized Key: After running the exploit, confirm the extracted string matches the value set during the "Test Data Setup" phase.
  2. Verify via CLI:
    wp option get betterdocs_settings --format=json | jq -r '.openai_api_key'
    
    Ensure the CLI output matches the data extracted via the HTTP request/Browser evaluation.

9. Alternative Approaches

If the scripts() function does not enqueue on the standard Post Editor for Contributors:

  1. Check Knowledge Base Editor: Navigate to wp-admin/post-new.php?post_type=docs. Contributors may have permission to create "Docs" post types if configured, triggering the same script enqueuing.
  2. Search for other Admin Hooks: If admin_enqueue_scripts is not the only entry point, check wp_enqueue_scripts (frontend) to see if the key is leaked to unauthenticated users or standard subscribers.
  3. Check Plugin Dashboard: Some plugins enqueue admin scripts on their own dashboard page. If Contributors can access the BetterDocs "Docs" list, they might trigger the script there.
Research Findings
Static analysis — not yet PoC-verified

Summary

BetterDocs versions up to 4.3.3 expose the OpenAI API key stored in plugin settings to any authenticated user with Contributor-level access or higher. This occurs because the plugin's scripts() function localizes sensitive configuration data into a global JavaScript variable that is rendered in the HTML source of administrative pages.

Vulnerable Code

// From the BetterDocs admin or asset management class (e.g., includes/Admin/Assets.php)
public function scripts($hook) {
    // ... (code to check if scripts should be enqueued)

    $settings = get_option('betterdocs_settings');

    // The entire settings array, including the 'openai_api_key', is localized to the script
    wp_localize_script('betterdocs-admin', 'betterdocs_admin_params', $settings);
}

Security Fix

--- a/includes/Admin/Assets.php
+++ b/includes/Admin/Assets.php
@@ -100,6 +100,9 @@
 
         $settings = get_option('betterdocs_settings', []);
 
+        // Remove sensitive keys before localizing to the frontend/admin scripts
+        if (isset($settings['openai_api_key'])) {
+            unset($settings['openai_api_key']);
+        }
+
         wp_localize_script('betterdocs-admin', 'betterdocs_admin_params', $settings);

Exploit Outline

The exploit is a passive information exposure that requires Contributor-level authentication. An attacker logs into the WordPress dashboard and navigates to any page where the BetterDocs admin scripts are enqueued, such as the Post Editor (wp-admin/post-new.php). By inspecting the HTML source code of the page, the attacker can find a <script> block containing the 'betterdocs_admin_params' JavaScript object. Within this object, the 'openai_api_key' value is exposed in plaintext. No specific payload is required beyond accessing the administrative interface.

Check if your site is affected.

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