CVE-2026-24620

Landing Page Builder <= 1.5.3.4 - Authenticated (Author+) Stored Cross-Site Scripting

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

Description

The Landing Page Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.5.3.4 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.5.3.4
PublishedJanuary 10, 2026
Last updatedApril 15, 2026
Affected pluginpage-builder-add

What Changed in the Fix

Changes introduced in v1.5.3.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-24620 (Landing Page Builder Stored XSS) ## 1. Vulnerability Summary The **Landing Page Builder** plugin (versions <= 1.5.3.4) contains a stored Cross-Site Scripting (XSS) vulnerability within its AJAX data saving mechanism. The `ULPB_Ajax_Requests::ulpb_admin_…

Show full research plan

Exploitation Research Plan: CVE-2026-24620 (Landing Page Builder Stored XSS)

1. Vulnerability Summary

The Landing Page Builder plugin (versions <= 1.5.3.4) contains a stored Cross-Site Scripting (XSS) vulnerability within its AJAX data saving mechanism. The ULPB_Ajax_Requests::ulpb_admin_ajax function, which handles page content updates, fails to sanitize specific keys within the JSON-encoded payload. An attacker with Editor-level access (or Author, depending on specific capability mappings) can inject arbitrary JavaScript into a landing page.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: ulpb_admin_data
  • HTTP Method: POST
  • Parameter: defaults (A JSON-encoded string containing page data).
  • Authentication: Required. The code checks current_user_can('editor') || current_user_can('administrator').
  • Nonce Requirement: None. Line 160 of admin/classes/ajax-requests-class.php explicitly sets $nonce = false; and the subsequent check if ( $nonce ) will never trigger a die.

3. Code Flow

  1. Entry Point: The AJAX action wp_ajax_ulpb_admin_data is registered in ULPB_Ajax_Requests::_hooks() and calls ulpb_admin_ajax().
  2. Method Detection: The function checks $_SERVER['REQUEST_METHOD']. If POST, it proceeds to line 178.
  3. Data Parsing:
    • $_POST['defaults'] is captured.
    • stripslashes() is applied.
    • json_decode(..., true) converts the string into an associative array $data.
  4. Vulnerable Sanitization Logic:
    • At line 204, the plugin defines cleanDataArray(&$value, $key, $allowed_html).
    • At line 214, it checks an allowlist: $contentKeysArr = array('accContent', 'widgetTextContent', 'wltc','fbTextContent','formCustomHTML','POcustomCSS','POcustomJS','widgetContent', ...);.
    • Sink: If the $key matches any string in this array, no sanitization is performed. The code branch is empty: if(in_array($key, $contentKeysArr)){ }.
    • If the key is not in the list, it uses esc_textarea($value).
  5. Storage: The $data is processed and eventually stored using update_post_meta($page_id, 'ULPB_DATA', $data).
  6. Execution: When a user views the landing page associated with the page_id, the raw content from widgetTextContent or POcustomJS is rendered in the HTML, executing the script.

4. Nonce Acquisition Strategy

Based on the source code analysis of admin/classes/ajax-requests-class.php:

  • Result: No nonce is required.
  • Verification: Line 160: $nonce = false;. Line 161: if ( $nonce ) { die( 'Invalid Nonce' ); }. This check is logically impossible to trigger.

5. Exploitation Strategy

The goal is to use an Editor account to update a landing page with a malicious payload.

Step-by-Step Plan

  1. Login: Authenticate as an Editor.
  2. Create/Identify Page: Create a new Landing Page (post type ulpb_post) or identify an existing one and capture its ID.
  3. Craft Payload: Create a JSON structure for the defaults parameter.
    • Key: pageID (must match the target page ID).
    • Key: widgetTextContent (contains the XSS payload).
    • Key: pageOptions (required to satisfy logic like $data['pageOptions']['pageSeoName']).
  4. Submit Update: Send a POST request to admin-ajax.php.
  5. Trigger: Navigate to the public URL of the Landing Page.

Target HTTP Request

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=ulpb_admin_data&defaults={"pageID":"[TARGET_PAGE_ID]","postType":"ulpb_post","pageStatus":"publish","pageOptions":{"pageSeoName":"XSS Test","loadWpFooterTwo":"true"},"widgetTextContent":"<script>alert(document.domain)</script>"}

6. Test Data Setup

  1. Target User: Create an Editor user (e.g., attacker_editor).
  2. Target Page:
    wp post create --post_type=ulpb_post --post_title="Vulnerable Page" --post_status=publish --post_author=[EDITOR_ID]
    
  3. Capture ID: Store the ID of the created post.

7. Expected Results

  • The AJAX response should be empty or a success indicator (the code does not show a specific JSON return before exiting).
  • When navigating to /?post_type=ulpb_post&p=[ID], the browser should execute alert(document.domain).

8. Verification Steps

  1. Database Check: Verify the payload is stored in post_meta.
    wp post meta get [ID] ULPB_DATA
    
  2. HTML Inspection: Fetch the page content and check for the raw script tag.
    # Using http_request to fetch the page and grep for the script
    # Response should contain: <script>alert(document.domain)</script>
    

9. Alternative Approaches

If widgetTextContent is blocked or handled differently by the frontend renderer, try these keys from the identified contentKeysArr:

  • POcustomJS: This is explicitly meant for JavaScript and is likely included in a <script> block.
    • Payload for POcustomJS: alert('XSS_FROM_CUSTOM_JS') (Note: do not include tags if the plugin wraps it).
  • formCustomHTML: Useful if the page uses a form builder widget.
    • Payload: <img src=x onerror=alert(1)>
  • btnLink: If the button link is not sanitized, use javascript:alert(1).

Alternative Payload for POcustomJS

{
  "pageID": "[ID]",
  "postType": "ulpb_post",
  "pageOptions": {"pageSeoName": "JS Test"},
  "POcustomJS": "alert('XSS')"
}

Check if your site is affected.

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