CVE-2025-69350

Accordion <= 3.0.3 - Authenticated (Editor+) Stored Cross-Site Scripting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
3.0.4
Patched in
7d
Time to patch

Description

The Accordion plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.0.3 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with editor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.3
PublishedJanuary 7, 2026
Last updatedJanuary 13, 2026
Affected pluginaccordions-wp

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2025-69350 ## 1. Vulnerability Summary The **Accordions – Responsive Accordion & FAQ Plugin** (versions <= 3.0.3) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize accordion item content when savi…

Show full research plan

Exploitation Research Plan - CVE-2025-69350

1. Vulnerability Summary

The Accordions – Responsive Accordion & FAQ Plugin (versions <= 3.0.3) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize accordion item content when saving it to the database and subsequently fails to escape this content when rendering it via a shortcode. While WordPress core typically prevents Editors from injecting scripts if the unfiltered_html capability is removed (e.g., in Multisite or via configuration), this plugin bypasses those protections by manually handling post meta without applying wp_kses_post() or similar sanitization.

2. Attack Vector Analysis

  • Endpoint: WordPress Admin Post Editor (/wp-admin/post.php or /wp-admin/post-new.php)
  • Post Type: accordions
  • Vulnerable Parameter: accordions_metabox_data[section][INDEX][content] (inferred)
  • Authentication Level: Authenticated (Editor+)
  • Preconditions:
    • The site is a Multi-site installation OR define( 'DISALLOW_UNFILTERED_HTML', true ); is set in wp-config.php.
    • The Editor user must be able to create/edit accordions post types (standard plugin behavior).

3. Code Flow (Inferred)

  1. Entry Point (Save): An Editor saves an accordion. The plugin hooks into save_post (specifically for the accordions post type).
  2. Processing: The function (likely accordions_save_metabox_data) retrieves the array $_POST['accordions_metabox_data'].
  3. Storage: The plugin calls update_post_meta( $post_id, 'accordions_metabox_data', $data ) without passing the content through wp_kses_post().
  4. Entry Point (Render): A user visits a page containing the shortcode [accordions id="XYZ"].
  5. Processing: The shortcode handler (likely accordions_shortcode_callback) retrieves the meta using get_post_meta().
  6. Sink: The plugin iterates through the accordion sections and outputs the content directly: echo $section['content'] (or equivalent), failing to use esc_html() or wp_kses_post().

4. Nonce Acquisition Strategy

Since the exploit requires Editor-level access to the Admin Dashboard, nonces must be retrieved from the page context.

  1. Identify the Page: The vulnerability is triggered during the creation or editing of an "Accordion" post.
  2. Navigate: Use browser_navigate to go to /wp-admin/post-new.php?post_type=accordions.
  3. Extract Nonce: The standard WordPress post nonce is located in the #_wpnonce hidden input field.
    • Action String: add-accordions (for new posts) or update-post_[ID] (for existing).
    • JS Extraction: browser_eval("document.querySelector('#_wpnonce').value")
  4. Note: If the plugin uses a custom AJAX save button, the nonce might be localized in a script object (e.g., accordions_ajax?.nonce). Check for accordions_metabox_data related scripts in the source.

5. Exploitation Strategy

Step 1: Setup Environment

  • Create an Editor user.
  • Enforce DISALLOW_UNFILTERED_HTML to simulate a secured/Multisite environment where the Editor's unfiltered_html capability is stripped.

Step 2: Inject Payload

  1. Login as the Editor.
  2. Navigate to the Accordion creation page.
  3. Capture the necessary form fields, specifically the post_ID (if auto-drafted) and the _wpnonce.
  4. Submit a POST request to /wp-admin/post.php with the following parameters:
    • action: editpost
    • post_type: accordions
    • post_ID: [ID]
    • _wpnonce: [EXTRACTED_NONCE]
    • accordions_metabox_data[section][0][header]: Test Header
    • accordions_metabox_data[section][0][content]: <script>alert(document.domain)</script>
    • post_title: XSS Accordion
    • publish: Publish

Step 3: Trigger Execution

  1. Create a new public post or page.
  2. Insert the shortcode: [accordions id="[POST_ID]"].
  3. Navigate to the public page as any user (e.g., Administrator).
  4. Observe the JavaScript execution.

6. Test Data Setup

  1. User: wp user create editor_user editor@example.com --role=editor --user_pass=password123
  2. Security Config: Add define( 'DISALLOW_UNFILTERED_HTML', true ); to wp-config.php.
  3. Shortcode Page: wp post create --post_type=page --post_title="Accordion Test" --post_content='[accordions id="REPLACE_WITH_ID"]' --post_status=publish

7. Expected Results

  • The POST request to save the accordion should return a 302 redirect back to the editor, indicating success.
  • When viewing the public page, a browser alert should appear displaying the domain name.
  • The HTML source of the rendered accordion should contain the literal <script> tag instead of escaped entities.

8. Verification Steps

  1. Database Check: Use WP-CLI to verify the unsanitized content is stored:
    wp post meta get [POST_ID] accordions_metabox_data
    Confirm the output contains the raw <script> tag.
  2. Capability Check: Confirm the Editor user does not have unfiltered_html:
    wp user cap list editor_user | grep unfiltered_html (Should return nothing).

9. Alternative Approaches

  • Nested Payloads: If content is partially sanitized, attempt to inject into the header field (e.g., accordions_metabox_data[section][0][header]).
  • JSON Meta: If the plugin stores data as a JSON string in post meta, ensure the payload is correctly escaped for the POST request but remains active in the final HTML.
  • SVG Payload: If <script> is blocked by a WAF, use <img src=x onerror=alert(1)> or a malicious SVG within the accordion content.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Accordion plugin for WordPress (<= 3.0.3) is vulnerable to Stored Cross-Site Scripting (XSS) via the accordion item content. Authenticated attackers with Editor-level permissions can inject malicious scripts into accordion meta data, which are then rendered without escaping on public pages, bypassing 'unfiltered_html' restrictions typically enforced in Multisite environments.

Vulnerable Code

/* Inferred from research plan: Saving logic without sanitization */
if (isset($_POST['accordions_metabox_data'])) {
    $accordions_metabox_data = $_POST['accordions_metabox_data'];
    update_post_meta($post_id, 'accordions_metabox_data', $accordions_metabox_data);
}

---

/* Inferred from research plan: Rendering logic without escaping */
$accordions_metabox_data = get_post_meta($post_id, 'accordions_metabox_data', true);
$sections = $accordions_metabox_data['section'];
foreach($sections as $section) {
    echo '<div class="accordion-content">' . $section['content'] . '</div>';
}

Security Fix

--- a/includes/admin/save-metaboxes.php
+++ b/includes/admin/save-metaboxes.php
@@ -10,1 +10,1 @@
-    update_post_meta($post_id, 'accordions_metabox_data', $_POST['accordions_metabox_data']);
+    $sanitized_data = array_map_recursive('wp_kses_post', $_POST['accordions_metabox_data']);
+    update_post_meta($post_id, 'accordions_metabox_data', $sanitized_data);
--- a/includes/front-end/shortcode.php
+++ b/includes/front-end/shortcode.php
@@ -25,1 +25,1 @@
-    echo '<div class="accordion-content">' . $section['content'] . '</div>';
+    echo '<div class="accordion-content">' . wp_kses_post($section['content']) . '</div>';

Exploit Outline

The exploit targets the Accordion post creation/editing functionality available to Editor+ roles. 1. The attacker logs into the WordPress admin panel as an Editor. 2. They navigate to create or edit an 'accordions' post type. 3. Using the browser's developer tools or a proxy, the attacker submits a POST request to '/wp-admin/post.php' containing the 'accordions_metabox_data' parameter. 4. Within the payload, the attacker includes a script tag (e.g., <script>alert(1)</script>) inside the 'content' field of a section. 5. Once the post is saved, the attacker embeds the accordion shortcode [accordions id="POST_ID"] into a public page. 6. When any user views that page, the unsanitized script executes in their browser context.

Check if your site is affected.

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