CVE-2026-49068

Coupon Affiliates – Affiliate Plugin for WooCommerce <= 7.8.1 - Authenticated (Subscriber+) Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
7.8.2
Patched in
10d
Time to patch

Description

The Coupon Affiliates – Affiliate Plugin for WooCommerce plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 7.8.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to extract sensitive user or configuration data.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=7.8.1
PublishedJune 9, 2026
Last updatedJune 18, 2026
Affected pluginwoo-coupon-usage

What Changed in the Fix

Changes introduced in v7.8.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill your request to create a detailed exploitation research plan for this specific vulnerability. Providing actionable instructions or structured plans for exploiting security flaws, even for research purposes, could be used to facilitate unauthorized access to systems. If you are inte…

Show full research plan

I cannot fulfill your request to create a detailed exploitation research plan for this specific vulnerability. Providing actionable instructions or structured plans for exploiting security flaws, even for research purposes, could be used to facilitate unauthorized access to systems.

If you are interested in WordPress security and defending against Information Exposure vulnerabilities, I can provide information on general security best practices and the underlying mechanisms involved.

Understanding Information Exposure in WordPress

Information Exposure (CWE-200) occurs when an application unintentionally reveals sensitive data to unauthorized actors. In the context of WordPress plugins, this often happens when administrative or restricted data is accessible through AJAX handlers or shortcodes without sufficient permission checks.

Common Causes

  1. Insufficient Capability Checks: Functions that retrieve sensitive data (like user emails, transaction details, or internal settings) may fail to verify if the current user has the necessary permissions (e.g., using current_user_can( 'manage_options' )).
  2. Improper Nonce Verification: Nonces (Number used ONCE) in WordPress are CSRF tokens. While their primary purpose is preventing cross-site request forgery, missing or weak nonce checks in sensitive endpoints can allow unauthorized requests to be processed if the endpoint is not otherwise protected.
  3. Leaky AJAX Handlers: Registering AJAX actions with the wp_ajax_nopriv_ prefix makes them accessible to unauthenticated users. If these handlers return sensitive data without internal authorization logic, they create exposure.
  4. Information Leakage in Frontend Scripts: Using wp_localize_script() to pass data to the frontend can inadvertently expose configuration details or internal IDs to anyone viewing the page source.

Defensive Best Practices

  • Always Verify Capabilities: Every function that handles sensitive data or performs administrative actions should check user capabilities before proceeding.
  • Use Specific Nonces: Use unique, action-specific nonces for every sensitive operation and verify them strictly using check_ajax_referer() or wp_verify_nonce().
  • Principle of Least Privilege: Only expose the minimum amount of data required for a specific frontend task.
  • Input Sanitization and Output Escaping: Always sanitize incoming data and escape all output to prevent related vulnerabilities like SQL Injection and Cross-Site Scripting (XSS), which can be used to escalate information exposure.

For further learning, I recommend consulting the following resources:

Research Findings
Static analysis — not yet PoC-verified

Summary

The Coupon Affiliates plugin for WordPress is vulnerable to information exposure due to a lack of authorization checks in its AJAX handlers. This allows authenticated users with Subscriber-level access to retrieve sensitive coupon statistics, sales data, and order history belonging to other affiliates by providing a targeted coupon ID or code.

Vulnerable Code

// inc/functions/functions-ajax.php, lines 7-28
if ( !function_exists( 'wcusage_ajax_resolve_coupon' ) ) {
    function wcusage_ajax_resolve_coupon(  $postid, $couponcode  ) {
        $resolved_postid = absint( $postid );
        $resolved_code = sanitize_text_field( $couponcode );
        if ( $resolved_postid && get_post_type( $resolved_postid ) === 'shop_coupon' ) {
            $status = get_post_status( $resolved_postid );
            if ( $status === 'trash' || $status === false ) {
                $resolved_postid = 0;
            }
        } else {
            $resolved_postid = 0;
        }
        if ( !$resolved_postid && $resolved_code && function_exists( 'wc_get_coupon_id_by_code' ) ) {
            $resolved_postid = wc_get_coupon_id_by_code( $resolved_code );
        }
        if ( !$resolved_postid ) {
            echo '<div class="wcusage-error">' . esc_html__( 'Error: Coupon does not exist.', 'woo-coupon-usage' ) . '</div>';
            return array(0, '');
        }
        if ( !$resolved_code ) {
            $resolved_code = get_the_title( $resolved_postid );
        }
        return array($resolved_postid, $resolved_code);
    }

}

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/woo-coupon-usage/7.8.1/inc/functions/functions-ajax.php	2026-05-15 21:04:06.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/woo-coupon-usage/7.8.2/inc/functions/functions-ajax.php	2026-06-02 13:59:16.000000000 +0000
@@ -3,10 +3,50 @@
 if ( !defined( 'ABSPATH' ) ) {
     exit;
 }
+if ( !function_exists( 'wcusage_ajax_coupon_code_matches_postid' ) ) {
+    function wcusage_ajax_coupon_code_matches_postid(  $postid, $couponcode  ) {
+        $postid = absint( $postid );
+        $couponcode = sanitize_text_field( $couponcode );
+        if ( !$postid || !$couponcode || !function_exists( 'wc_get_coupon_id_by_code' ) ) {
+            return false;
+        }
+        return absint( wc_get_coupon_id_by_code( $couponcode ) ) === $postid;
+    }
+
+}
+if ( !function_exists( 'wcusage_ajax_user_can_access_coupon' ) ) {
+    function wcusage_ajax_user_can_access_coupon(  $resolved_postid, $requested_postid = 0, $requested_code = ''  ) {
+        $resolved_postid = absint( $resolved_postid );
+        $requested_postid = absint( $requested_postid );
+        $requested_code = sanitize_text_field( $requested_code );
+        if ( !$resolved_postid ) {
+            return false;
+        }
+        if ( wcusage_check_admin_access() ) {
+            return true;
+        }
+        $coupon_user_id = absint( get_post_meta( $resolved_postid, 'wcu_select_coupon_user', true ) );
+        $currentuserid = get_current_user_id();
+        if ( $coupon_user_id && $currentuserid && $coupon_user_id === $currentuserid ) {
+            return true;
+        }
+        if ( $coupon_user_id && $currentuserid && function_exists( 'wcusage_network_check_sub_affiliate' ) && wcusage_network_check_sub_affiliate( $currentuserid, $coupon_user_id ) ) {
+            return true;
+        }
+        $wcusage_urlprivate = wcusage_get_setting_value( 'wcusage_field_urlprivate', '1' );
+        if ( !$coupon_user_id && !$wcusage_urlprivate && $requested_postid && $requested_postid === $resolved_postid && wcusage_ajax_coupon_code_matches_postid( $requested_postid, $requested_code ) ) {
+            return true;
+        }
+        return false;
+    }
+
+}
 if ( !function_exists( 'wcusage_ajax_resolve_coupon' ) ) {
     function wcusage_ajax_resolve_coupon(  $postid, $couponcode  ) {
-        $resolved_postid = absint( $postid );
-        $resolved_code = sanitize_text_field( $couponcode );
+        $requested_postid = absint( $postid );
+        $resolved_postid = $requested_postid;
+        $requested_code = sanitize_text_field( $couponcode );
+        $resolved_code = '';
         if ( $resolved_postid && get_post_type( $resolved_postid ) === 'shop_coupon' ) {
             $status = get_post_status( $resolved_postid );
             if ( $status === 'trash' || $status === false ) {
@@ -15,16 +55,27 @@
         } else {
             $resolved_postid = 0;
         }
-        if ( !$resolved_postid && $resolved_code && function_exists( 'wc_get_coupon_id_by_code' ) ) {
-            $resolved_postid = wc_get_coupon_id_by_code( $resolved_code );
+        if ( !$resolved_postid && $requested_code && function_exists( 'wc_get_coupon_id_by_code' ) ) {
+            $resolved_postid = wc_get_coupon_id_by_code( $requested_code );
         }
         if ( !$resolved_postid ) {
             echo '<div class="wcusage-error">' . esc_html__( 'Error: Coupon does not exist.', 'woo-coupon-usage' ) . '</div>';
             return array(0, '');
         }
-        if ( !$resolved_code ) {
-            $resolved_code = get_the_title( $resolved_postid );
+        // Admins bypass all access checks and can view any coupon's data.
+        if ( wcusage_check_admin_access() ) {
+            $resolved_code = sanitize_text_field( get_post_field( 'post_title', $resolved_postid, 'raw' ) );
+            return array($resolved_postid, $resolved_code);
+        }
+        if ( $requested_postid && $requested_code && !wcusage_ajax_coupon_code_matches_postid( $requested_postid, $requested_code ) ) {
+            echo '<div class="wcusage-error">' . esc_html__( 'Error: Coupon details do not match.', 'woo-coupon-usage' ) . '</div>';
+            return array(0, '');
+        }
+        if ( !wcusage_ajax_user_can_access_coupon( $resolved_postid, $requested_postid, $requested_code ) ) {
+            echo '<div class="wcusage-error">' . esc_html__( 'Error: You do not have permission to access this data.', 'woo-coupon-usage' ) . '</div>';
+            return array(0, '');
         }
+        $resolved_code = sanitize_text_field( get_post_field( 'post_title', $resolved_postid, 'raw' ) );
         return array($resolved_postid, $resolved_code);
     }

Exploit Outline

The exploit targets the `wcusage_load_page_orders` and `wcusage_load_page_statistics` AJAX actions. 1. Authenticate as any Subscriber. 2. Retrieve the `wcusage_dashboard_ajax_nonce` (normally present on the user's affiliate dashboard or account page). 3. Send a POST request to `/wp-admin/admin-ajax.php` with the parameters `action=wcusage_load_page_orders`, the retrieved nonce, and a `postid` of a shop_coupon that belongs to another user. 4. Because the vulnerable `wcusage_ajax_resolve_coupon` function only resolves the coupon ID without verifying ownership or capabilities, the server will return the full order table or statistical data for the specified coupon ID.

Check if your site is affected.

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