FSM Custom Featured Image Caption <= 1.25.1 - Authenticated (Author+) Stored Cross-Site Scripting
Description
The FSM Custom Featured Image Caption plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.25.1 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
<=1.25.1This research plan details the analysis and exploitation strategy for **CVE-2026-39693**, a Stored Cross-Site Scripting vulnerability in the **FSM Custom Featured Image Caption** plugin. --- ### 1. Vulnerability Summary The **FSM Custom Featured Image Caption** plugin allows users to add custom te…
Show full research plan
This research plan details the analysis and exploitation strategy for CVE-2026-39693, a Stored Cross-Site Scripting vulnerability in the FSM Custom Featured Image Caption plugin.
1. Vulnerability Summary
The FSM Custom Featured Image Caption plugin allows users to add custom text/captions to featured images. The vulnerability exists because the plugin fails to sanitize the caption input during the saving process and fails to escape the output when rendering the caption on the frontend. An attacker with Author level permissions or higher can inject malicious JavaScript into the caption field, which executes in the context of any user (including Administrators) viewing the affected post.
2. Attack Vector Analysis
- Endpoint: WordPress Post Editor (
/wp-admin/post.php) or the Media Library. - Vulnerable Parameter: A custom metadata field likely named
fsm_captionor similar (inferred based on plugin functionality). - Authentication: Required (Author+). Authors have the
edit_postsandupload_filescapabilities, allowing them to set featured images and modify their metadata. - Preconditions: The plugin must be active, and a post must have a featured image with the "FSM Caption" field populated.
3. Code Flow (Inferred)
Input (Admin Side):
- The plugin registers a meta box or a field in the Media Library UI using hooks like
add_meta_boxesorattachment_fields_to_edit. - When a post is saved, the plugin hooks into
save_postoredit_attachment. - It retrieves the caption from
$_POST['fsm_caption'](inferred) and saves it usingupdate_post_meta($post_id, '_fsm_caption', $payload)without callingsanitize_text_field()orwp_kses().
- The plugin registers a meta box or a field in the Media Library UI using hooks like
Output (Frontend Side):
- The plugin hooks into the featured image rendering process, likely via the
post_thumbnail_htmlfilter or a custom shortcode. - It retrieves the stored metadata using
get_post_meta($post_id, '_fsm_caption', true). - It appends this metadata to the HTML output using string concatenation and returns/echoes it without using
esc_html()oresc_attr().
- The plugin hooks into the featured image rendering process, likely via the
4. Nonce Acquisition Strategy
Since the vulnerability involves an Authenticated Author, we must navigate the WordPress admin dashboard to obtain the necessary nonces for post updates.
- Identify the Field: Navigate to the "Edit Post" screen of an existing post.
- Locate Nonces: The standard WordPress post update uses the
_wpnoncefield found in the#postform. - Variable Identification:
- If the plugin uses AJAX to save captions, search the source for
wp_localize_script. - Target JS Object:
window.fsm_caption_paramsor similar (inferred). - Command:
browser_eval("window.fsm_params?.nonce")
- If the plugin uses AJAX to save captions, search the source for
- Standard Post Save: If the field is part of the standard post meta box, no special nonce beyond the core
_wpnonceandpost_IDis needed to submit the update viapost.php.
5. Exploitation Strategy
Step 1: Discovery of Parameter Name
Use browser_navigate to an edit post page and inspect the DOM for fields related to "FSM" or "Caption" near the featured image area.
- Target Field Search:
document.querySelectorAll('input[name*="fsm"], textarea[name*="fsm"]')
Step 2: Inject Payload
Perform an HTTP POST request to update the post metadata.
- Request Type: POST
- URL:
https://TARGET/wp-admin/post.php - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:[POST_ID]_wpnonce:[EXTRACTED_NONCE]fsm_caption(or discovered name):<script>alert(document.domain)</script>post_title:Test Post
Step 3: Trigger Execution
Navigate to the frontend URL of the modified post.
6. Test Data Setup
- User Creation: Create a user with the
authorrole.wp user create attacker attacker@example.com --role=author --user_pass=password123
- Content Creation: Create a post and set a featured image.
wp post create --post_type=post --post_status=publish --post_title="Vulnerable Post" --post_author=[AUTHOR_ID]- Upload an image and set it as
_thumbnail_idfor that post.
- Plugin Configuration: Ensure the plugin setting "Show caption on frontend" (if it exists) is enabled.
7. Expected Results
- The POST request should return a
302 Redirectback to the post edit page withmessage=4(Post updated). - When viewing the post frontend, the HTML source should contain the raw payload:
<div class="fsm-caption"><script>alert(document.domain)</script></div>. - A JavaScript alert box should appear in the browser.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored in post meta.
wp post meta list [POST_ID] --keys=fsm_caption(or the discovered key)
- HTML Inspection: Use the
http_requesttool to fetch the frontend post and grep for the payload.- Look for the absence of
<or>encoding around the script tag.
- Look for the absence of
9. Alternative Approaches
- Media Library Injection: If the plugin adds the field to the "Edit Attachment" page instead of the "Edit Post" page, the attack would target
/wp-admin/post.php?post=[ATTACHMENT_ID]&action=edit. - Shortcode Exploitation: If the plugin provides a shortcode like
[fsm_featured_image_caption], test if the XSS can be triggered by placing the payload inside the shortcode attributes:[fsm_featured_image_caption caption="<img src=x onerror=alert(1)>"]. Authors can typically embed shortcodes in post content.
Summary
The FSM Custom Featured Image Caption plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'fsm_caption' field. Authenticated users with Author-level permissions or higher can inject malicious JavaScript into this metadata field, which is subsequently rendered on the frontend without proper sanitization or output escaping.
Vulnerable Code
// Inferred from plugin functionality and research plan // File: fsm-custom-featured-image-caption.php // Saving logic without sanitization function fsm_save_caption( $post_id ) { if ( isset( $_POST['fsm_caption'] ) ) { update_post_meta( $post_id, '_fsm_caption', $_POST['fsm_caption'] ); } } add_action( 'save_post', 'fsm_save_caption' ); --- // Rendering logic without escaping function fsm_add_caption_to_thumbnail( $html, $post_id ) { $caption = get_post_meta( $post_id, '_fsm_caption', true ); if ( ! empty( $caption ) ) { $html .= '<div class="fsm-caption">' . $caption . '</div>'; } return $html; } add_filter( 'post_thumbnail_html', 'fsm_add_caption_to_thumbnail', 10, 2 );
Security Fix
@@ -10,7 +10,7 @@ function fsm_save_caption( $post_id ) { if ( isset( $_POST['fsm_caption'] ) ) { - update_post_meta( $post_id, '_fsm_caption', $_POST['fsm_caption'] ); + update_post_meta( $post_id, '_fsm_caption', sanitize_text_field( $_POST['fsm_caption'] ) ); } } @@ -20,7 +20,7 @@ function fsm_add_caption_to_thumbnail( $html, $post_id ) { $caption = get_post_meta( $post_id, '_fsm_caption', true ); if ( ! empty( $caption ) ) { - $html .= '<div class="fsm-caption">' . $caption . '</div>'; + $html .= '<div class="fsm-caption">' . esc_html( $caption ) . '</div>'; } return $html; }
Exploit Outline
To exploit this vulnerability, an attacker with Author-level privileges must navigate to the WordPress Post Editor for any post they have permission to edit. By identifying the 'fsm_caption' field (often located near the Featured Image meta box) or by intercepting the POST request to 'wp-admin/post.php', the attacker can insert a payload such as '<script>alert(document.domain)</script>' into the caption parameter. When the post is saved, the script is stored in the database. Any user viewing the post on the frontend will trigger the execution of the script in their browser context, allowing for session hijacking or further administrative actions if the victim is an administrator.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.