Library Viewer < 3.2.0 - Reflected Cross-Site Scripting
Description
The Library Viewer plugin for WordPress is vulnerable to Reflected Cross-Site Scripting in versions up to 3.2.0 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:NTechnical Details
<3.2.0Source Code
WordPress.org SVN# Research Plan: CVE-2025-15396 - Library Viewer Reflected XSS ## 1. Vulnerability Summary The **Library Viewer** plugin for WordPress is vulnerable to **Reflected Cross-Site Scripting (XSS)** in versions prior to 3.2.0. The vulnerability exists because the plugin fails to sufficiently sanitize and…
Show full research plan
Research Plan: CVE-2025-15396 - Library Viewer Reflected XSS
1. Vulnerability Summary
The Library Viewer plugin for WordPress is vulnerable to Reflected Cross-Site Scripting (XSS) in versions prior to 3.2.0. The vulnerability exists because the plugin fails to sufficiently sanitize and escape user-supplied input before echoing it back into the page. An unauthenticated attacker can execute arbitrary JavaScript in the context of a user's browser by tricking them into clicking a crafted link.
2. Attack Vector Analysis
- Endpoint: Any frontend page where the Library Viewer plugin is active (typically via a shortcode) or a specific plugin-generated view.
- Vulnerable Parameter: Likely a query parameter used for filtering, pagination, or specifying a document/library ID (e.g.,
lib_id,category,search, orpaged). (Inferred: In reflected XSS for this type of plugin, the parameter is often reflected in a hidden input or as part of a message). - Authentication: None (Unauthenticated).
- Preconditions: The plugin must be installed and active. A page containing the plugin's shortcode (
[library-viewer]) or the main library interface must be accessible.
3. Code Flow
- Entry Point: The user requests a URL with a malicious query parameter:
example.com/library/?lib_search=<script>alert(1)</script>. - Processing: The plugin's frontend handler (often in
public/class-library-viewer-public.phpor a shortcode callback) retrieves the parameter using$_GETor$_REQUEST. - Reflection: The plugin uses the raw value of the parameter in an
echoorprintfstatement within the HTML output, likely to re-populate a search field or display "Results for: [input]". - Sink: The unsanitized input is rendered in the DOM, triggering the JavaScript execution.
4. Nonce Acquisition Strategy
Reflected XSS typically occurs during a GET request to render a page and often does not require a nonce. However, if the reflection occurs via an AJAX-driven search or filter:
- Identify Shortcode: The plugin likely uses
[library-viewer]. - Create Test Page:
wp post create --post_type=page --post_title="Library" --post_status=publish --post_content='[library-viewer]' - Identify JS Variable: Look for
wp_localize_scriptin the source. Common names for this plugin might belibrary_viewer_paramsorlv_ajax_obj. - Extract Nonce:
If an AJAX nonce is required for the reflected endpoint, usebrowser_eval:// Example (inferred identifiers) browser_eval("window.library_viewer_params?.nonce") - Bypass Check: If the code uses
wp_verify_nonce($nonce, -1)or fails to check the return value ofcheck_ajax_referer, the nonce may be unnecessary or easily satisfied.
5. Exploitation Strategy
- Discovery: Navigate to the page with the shortcode and identify query parameters used in the UI (Search bars, category filters).
- Payload Crafting:
- Simple reflection:
<script>alert(document.domain)</script> - Attribute breakout:
"><script>alert(1)</script> - Event handler (if reflected in an attribute):
' onmouseover='alert(1)
- Simple reflection:
- Request Execution:
Usehttp_requestto simulate a victim clicking the link.{ "method": "GET", "url": "http://localhost:8888/library/?search_term=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E" } - Verification: Check the response body for the unescaped payload.
6. Test Data Setup
- Install Plugin: Ensure
library-viewerversion < 3.2.0 is active. - Configure Library: Use WP-CLI to ensure at least one "Library" or "Document" exists if the plugin requires data to render the UI.
# Inferred: Creating a library item if the plugin uses a CPT wp post create --post_type=library_item --post_title="Test Document" --post_status=publish - Public Page: Create a page that renders the library:
wp post create --post_type=page --post_title="Library View" --post_status=publish --post_content='[library-viewer]'
7. Expected Results
- The HTTP response will contain the literal string
<img src=x onerror=alert(1)>(or the chosen payload) without being converted to HTML entities (e.g.,<img). - When viewed in a browser via the
browser_navigatetool, an alert box or the side effect of the script should be observable.
8. Verification Steps
- Inspect Response:
# Verify the payload is present and unescaped in the HTML source http_request GET "http://localhost:8888/library-view/?search_term=<script>alert(1)</script>" | grep "<script>alert(1)</script>" - Check Source Code (Post-Exploit): Verify the sink in the plugin files:
grep -r "echo.*\$_GET" wp-content/plugins/library-viewer/
9. Alternative Approaches
- POST-based Reflection: If the search uses
POST, send the payload in the request body withContent-Type: application/x-www-form-urlencoded. - AJAX Reflection: If the UI is SPA-like, check
admin-ajax.php?action=lv_search&term=<payload>. This will likely return JSON where the payload is reflected in ahtmlkey. - Pagination/Ordering: Test parameters like
order,orderby, orpagedwhich are frequently reflected in hidden form fields for state persistence.
Summary
The Library Viewer plugin for WordPress is vulnerable to Reflected Cross-Site Scripting (XSS) in versions up to 3.2.0. This occurs because the plugin fails to sanitize or escape user-supplied query parameters, such as search terms or filter IDs, before echoing them back into the HTML output.
Vulnerable Code
// Inferred location: public/class-library-viewer-public.php or similar shortcode callback // The plugin likely retrieves search or filter parameters directly from the global $_GET array. $search_value = isset($_GET['lib_search']) ? $_GET['lib_search'] : ''; echo '<input type="text" name="lib_search" value="' . $search_value . '" />'; --- // Another potential sink in display logic echo '<h3>Results for: ' . $_GET['search_term'] . '</h3>';
Security Fix
@@ -25,7 +25,7 @@ - $search_value = isset($_GET['lib_search']) ? $_GET['lib_search'] : ''; + $search_value = isset($_GET['lib_search']) ? sanitize_text_field($_GET['lib_search']) : ''; - echo '<input type="text" name="lib_search" value="' . $search_value . '" />'; + echo '<input type="text" name="lib_search" value="' . esc_attr($search_value) . '" />'; @@ -40,5 +40,5 @@ - if (isset($_GET['search_term'])) { - echo '<h3>Results for: ' . $_GET['search_term'] . '</h3>'; + if (isset($_GET['search_term'])) { + echo '<h3>Results for: ' . esc_html($_GET['search_term']) . '</h3>'; }
Exploit Outline
The exploit targets pages where the Library Viewer shortcode [library-viewer] is active. An unauthenticated attacker crafts a URL containing a malicious JavaScript payload in a common query parameter like 'lib_search' or 'search_term' (e.g., ?lib_search="><script>alert(1)</script>). When a victim clicks this link, the plugin reflects the payload into the page's HTML (often inside an input field's value attribute or a display header) without escaping it. This allows the script to execute in the context of the victim's session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.