BetterDocs <= 4.3.3 - Authenticated (Contributor+) Sensitive Information Exposure
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:NTechnical Details
<=4.3.3Source Code
WordPress.org SVN# 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.phporwp-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)
- Hook Registration: The plugin registers the
scripts()method to theadmin_enqueue_scriptshook. - Function Execution: When a user (e.g., a Contributor) loads the Post Editor,
admin_enqueue_scriptsfires. - Data Fetching: Inside
scripts(), the plugin retrieves settings usingget_option('betterdocs_settings')or a similar option key. - Insecure Localization: The function calls
wp_localize_script( $handle, $object_name, $l10n_array ). The$l10n_arrayis populated with settings that include theopenai_api_key. - Output: WordPress prints the data as a JSON object in a
<script>tag in the HTML head/footer. - 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
- 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 - Create Contributor:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123
Step 3: Execution Plan
- Login: Log in as the
attackeruser using thehttp_requestorbrowser_navigatetool. - Navigate to Editor: Navigate to
wp-admin/post-new.php. - Extract Data:
- Method A (HTML Parsing): Fetch the page HTML using
http_requestand search for the localized variable. - Method B (Browser Evaluation): Use
browser_evalto 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 - Method A (HTML Parsing): Fetch the page HTML using
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_keymust be present in the plugin's settings array (found inwp_options). - User Role: A user with the
contributorrole must be created and authenticated.
7. Expected Results
- The HTTP response for
wp-admin/post-new.phpwill 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
- Identify Localized Key: After running the exploit, confirm the extracted string matches the value set during the "Test Data Setup" phase.
- Verify via CLI:
Ensure the CLI output matches the data extracted via the HTTP request/Browser evaluation.wp option get betterdocs_settings --format=json | jq -r '.openai_api_key'
9. Alternative Approaches
If the scripts() function does not enqueue on the standard Post Editor for Contributors:
- 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. - Search for other Admin Hooks: If
admin_enqueue_scriptsis not the only entry point, checkwp_enqueue_scripts(frontend) to see if the key is leaked to unauthenticated users or standard subscribers. - 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.
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
@@ -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.