Responsive Pricing Table <= 5.1.12 - Authenticated (Author+) Stored Cross-Site Scripting
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:NTechnical Details
<=5.1.12Source Code
WordPress.org SVN# 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)
- Entry Point: An Author-level user submits a request to save or update a Pricing Table post.
- Processing (Admin): The plugin catches the save action (likely via the
save_posthook).- Vulnerable function (inferred): A function in
inc/admin-post-type.phporinc/metaboxes.phpreads theplan_iconsarray 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).
- Vulnerable function (inferred): A function in
- Sink (Frontend): A user visits a page containing the
[rtp_pricing_table id="..."]shortcode.- Vulnerable function (inferred): The shortcode handler in
inc/frontend-display.phpretrieves 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 usingesc_attr()oresc_html().
- Vulnerable function (inferred): The shortcode handler in
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.
- Identify Post Type: The pricing table post type is typically
rpt_pricing_table. - Create/Edit Page: Navigate to the "Add New" pricing table page.
- Extraction:
- Navigate to:
/wp-admin/post-new.php?post_type=rpt_pricing_table. - Use
browser_evalto extract the_wpnoncefrom the form and thepost_ID.
// To get the standard WordPress post nonce const nonce = document.querySelector('#_wpnonce')?.value; const postId = document.querySelector('#post_ID')?.value; return { nonce, postId }; - Navigate to:
- 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:editpostpost_ID:[POST_ID_FROM_STEP_4]_wpnonce:[NONCE_FROM_STEP_4]post_type:rpt_pricing_tablepost_title:XSS Test Tableplan_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
- Publish the Pricing Table.
- Create a new public post/page containing the shortcode for that table.
- Navigate to that page using
browser_navigate.
6. Test Data Setup
- User: Create a user with the Author role.
- Plugin Configuration: Ensure the "Responsive Pricing Table" plugin is installed and activated.
- 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]"].
- Create a draft pricing table to get a
7. Expected Results
- The
plan_iconsvalue will be saved to thewp_postmetatable 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
valueorclassattribute 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.phpis not the primary save method, check forwp_ajax_rpt_savehandlers.grep -r "wp_ajax_rpt" .
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
@@ -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 ); @@ -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.