Coupon Affiliates – Affiliate Plugin for WooCommerce <= 7.8.1 - Authenticated (Subscriber+) Information Exposure
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:NTechnical Details
<=7.8.1What Changed in the Fix
Changes introduced in v7.8.2
Source Code
WordPress.org SVNI 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
- 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' )). - 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.
- 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. - 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()orwp_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:
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
@@ -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.