# Stored XSS in Jetpack's Simple Payment Module by Contributors / Authors

Contributors/authors could set unprotected spay_formatted_price post meta on Jetpack product posts, rendered unsanitized by the [simple-payment] shortcode, yielding stored XSS against admins.

- Payout: hidden
- Severity: medium
- Program: Automattic
- Found by: simonscannell
- Disclosed: 2019-12-19
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/402753
- Library page: https://bugbountylibrary.com/r/h1%3A402753
- Tags: area:web domain:web impact:integrity impact:privesc kind:bounty platform:hackerone sev:medium tech:php tech:wordpress technique:source-review vuln:xss

**Target:** WordPress plugin shortcode renderer / custom post type post meta
**Root cause:** Product custom post type capabilities are set to edit_posts, letting contributors/authors create products and write arbitrary post meta; format_price returns spay_formatted_price verbatim and output_shortcode echoes it into HTML without escaping.
**Steps:**
1. As a contributor/author, create a product post (or use wp_ajax_add_meta) and set post meta `spay_formatted_price` to an XSS payload.
2. Reference the product via `[simple-payment id="<post_id>"]` in a post.
3. parse_shortcode reads spay_formatted_price via get_post_meta and passes it to format_price, which returns it unchanged.
4. output_shortcode echoes it into `<div class='...-price'><p>{$data['price']}</p></div>` unescaped, executing when an admin views the page.
**How found:** Noticed product CPT capabilities mapped to edit_posts, meaning contributors/authors can create products and write unprotected post meta; then traced the shortcode render path to find price echoed unsanitized.
**Exploitation:** Low-privileged contributor/author stores a payload in product post meta, then embeds the shortcode; when an admin views the rendered post the script executes in their session, enabling privilege escalation.
**Escalation:** Stored XSS against admin session leads to WordPress privilege escalation.
**Insight:** When a custom post type maps capabilities to low-priv roles like edit_posts, treat all its post meta as attacker-controlled and audit every render path (shortcodes, REST) for unescaped output of those meta values.

