CVE-2026-42744

Quads Ads Manager for Google AdSense <= 3.0.2 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
3.0.3
Patched in
5d
Time to patch

Description

The Quads Ads Manager for Google AdSense plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 3.0.2. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.0.2
PublishedMay 28, 2026
Last updatedJune 2, 2026
Affected pluginquick-adsense-reloaded

What Changed in the Fix

Changes introduced in v3.0.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-42744 (Quads Ads Manager) ## 1. Vulnerability Summary The **Quads Ads Manager for Google AdSense** plugin (up to version 3.0.2) is vulnerable to **Missing Authorization**. The function `quads_authorize_payment_success` in `includes/ad-selling-helper.php` is ho…

Show full research plan

Exploitation Research Plan: CVE-2026-42744 (Quads Ads Manager)

1. Vulnerability Summary

The Quads Ads Manager for Google AdSense plugin (up to version 3.0.2) is vulnerable to Missing Authorization. The function quads_authorize_payment_success in includes/ad-selling-helper.php is hooked to WordPress init. While it performs a check to ensure a user is logged in (is_user_logged_in()), it fails to verify if the user has administrative privileges or if the user is the owner of the specific advertisement order being processed. This allows any authenticated user (e.g., a Subscriber) to trigger the payment confirmation logic, marking advertisement slots as "paid" in the database without an actual transaction.

2. Attack Vector Analysis

  • Endpoint: Any frontend or backend page (since the function is hooked to init).
  • Method: GET
  • Action Hook: add_action( 'init', 'quads_authorize_payment_success' );
  • Required Parameters:
    • security: A valid WordPress nonce for the action 'security'.
    • status: Must be set to success.
    • ad_slot_id: The ID of the ad slot (must be > 0).
    • refId: A non-empty reference string (likely corresponds to the order ID based on the code's usage of $order_id).
    • user_id: The ID of the user whose ad is being marked as paid.
  • Authentication: Authenticated user (Subscriber or higher).

3. Code Flow

  1. Entry: WordPress initializes and triggers the init hook.
  2. Trigger: quads_authorize_payment_success() is called.
  3. Auth Check: is_user_logged_in() passes if the attacker is logged in as a Subscriber.
  4. Nonce Check: wp_verify_nonce( $_GET['security'], 'security' ) validates the token.
  5. Logic:
    • The code attempts to retrieve the user via get_user_by( 'id', $user_id ).
    • It queries the quads_adbuy_data table for the order details.
    • Vulnerability Sink: It calls $wpdb->update() on the quads_adbuy_data table, setting payment_status to 'paid'.
    • Side Effect: Sends a confirmation email to both the user and the administrator via wp_mail().

Note: In the provided source snippet, $order_id is used but not explicitly initialized from $_GET. It is likely that $order_id is mapped to $_GET['refId'] or assigned elsewhere in the full file.

4. Nonce Acquisition Strategy

The nonce action string is 'security'. To obtain this nonce as a Subscriber:

  1. Identify the Source: The quads_create_sellpage_on_activation() function indicates that a page with the shortcode [quads_buy_form] is created (usually at the slug /buy-adspace).
  2. Setup: If the page doesn't exist, create it:
    wp post create --post_type=page --post_title="Buy Ads" --post_status=publish --post_content='[quads_buy_form]'
  3. Extraction:
    • Log in as a Subscriber user.
    • Navigate to the page containing the [quads_buy_form].
    • Use browser_eval to search for the nonce. It may be in a hidden field or a localized JS variable.
    • Search Pattern: Look for wp_create_nonce( 'security' ) in the plugin source to find the exact JS localization key. If found in a form:
      browser_eval("document.querySelector('input[name=\"security\"]').value")

5. Exploitation Strategy

  1. Login: Authenticate as a Subscriber user.

  2. Capture Nonce: Visit the ad purchase page and extract the 'security' nonce.

  3. Identify Target: Determine a valid ad_slot_id and the database ID of an unpaid ad purchase (refId/order_id).

  4. Execute Attack: Send a GET request to the site root with the forged parameters.

    Request Example:

    GET /?status=success&ad_slot_id=1&refId=1&user_id=2&security=abc123def4 HTTP/1.1
    Host: target.local
    Cookie: [Subscriber Cookies]
    

6. Test Data Setup

  1. Enable Feature: Ensure the "Sellable Ads" feature is active.
  2. Create Order: Create an advertisement order in the database for the Subscriber user (ID 2).
    • Use WP-CLI to insert a record into the {wpdb->prefix}quads_adbuy_data table with payment_status = 'pending'.
  3. Find Slot: Ensure an ad slot exists (Post Type quads_ad_slot or similar, depending on plugin structure).

7. Expected Results

  • The plugin identifies the request as a successful payment.
  • The database record in wp_quads_adbuy_data for the given refId is updated: payment_status changes from pending to paid.
  • An email is triggered to the admin and the user confirming the payment.
  • The HTTP response may be a redirect or a success message, but the primary indicator is the database state.

8. Verification Steps

  1. Database Check: After the HTTP request, verify the payment status via WP-CLI:
    wp db query "SELECT payment_status FROM wp_quads_adbuy_data WHERE id = 1"
    Expected output: paid.
  2. Log Check: Check the WordPress mail log (if a logger is installed) to see if the "Ad Payment Confirmation" email was sent.

9. Alternative Approaches

  • Parameter Mapping: If refId does not map to the database id, check if the plugin uses $_GET['order_id'] (common in such plugins).
  • Unauthenticated Check: Verify if the is_user_logged_in() check is truly enforced in all versions. If missing, the attack becomes CVSS 9.8 (Critical).
  • CSRF: Since the action uses GET and requires a nonce, if the nonce is obtainable by an attacker but the is_user_logged_in() check is strict, the vulnerability could be leveraged as a CSRF against an Admin if they click a malicious link.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Quads Ads Manager for Google AdSense plugin is vulnerable to unauthorized access because the `quads_authorize_payment_success` function, hooked to the WordPress `init` action, lacks proper capability and ownership checks. This allows any authenticated user (such as a Subscriber) to mark an advertisement order as 'paid' in the database without completing a transaction.

Vulnerable Code

// includes/ad-selling-helper.php line 52
add_action( 'init', 'quads_authorize_payment_success' );
function quads_authorize_payment_success(){
    
    if ( !is_user_logged_in() ) {
        return false;
    }
    if( !isset( $_GET[ 'security' ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ 'security' ] ) ), 'security' )){ 
        return false;
    }
    if( isset( $_GET['status'] ) && $_GET['status']=='success' && isset( $_GET['ad_slot_id'] ) && $_GET['ad_slot_id'] > 0 && isset( $_GET['refId'] ) && $_GET['refId'] != "" && isset( $_GET['user_id'] ) && intval( $_GET['user_id'] ) >0 && !isset( $_GET['target'] )){
        // ... logic continues to update database -- line 92
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $table_name,
            array('payment_status' => 'paid' , 'payment_response'=> wp_json_encode($params)), // Data to update
            array('id' => $order_id , 'user_id'=>$user->ID) 
        );

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/quick-adsense-reloaded/3.0.2/includes/ad-selling-helper.php /home/deploy/wp-safety.org/data/plugin-versions/quick-adsense-reloaded/3.0.3/includes/ad-selling-helper.php
--- /home/deploy/wp-safety.org/data/plugin-versions/quick-adsense-reloaded/3.0.2/includes/ad-selling-helper.php	2026-04-16 14:19:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/quick-adsense-reloaded/3.0.3/includes/ad-selling-helper.php	2026-05-08 12:37:54.000000000 +0000
@@ -7,6 +7,52 @@
 if( !defined( 'ABSPATH' ) ) {
     exit;
 }
+
+/**
+ * Merge query args onto a return URL that may already contain ?key=value (e.g. ad_slot_id on the storefront).
+ *
+ * @param string $base_url Absolute URL.
+ * @param array  $args     Query parameters to merge.
+ * @return string
+ */
+function quads_add_return_query_args( $base_url, array $args ) {
+    $base_url = is_string( $base_url ) ? trim( $base_url ) : '';
+    if ( '' === $base_url ) {
+        return '';
+    }
+    return add_query_arg( $args, $base_url );
+}
+
+/**
+ * Absolute URL for the current request path plus sanitized query string (e.g. pre-selected ad slot).
+ *
+ * @return string
+ */
+function quads_get_checkout_redirect_base_url() {
+    global $wp;
+    $redirect_link = isset( $wp->request ) ? home_url( $wp->request ) : home_url( '/' );
+    if ( isset( $_SERVER['QUERY_STRING'] ) && is_string( $_SERVER['QUERY_STRING'] ) && $_SERVER['QUERY_STRING'] !== '' ) {
+        parse_str( wp_unslash( $_SERVER['QUERY_STRING'] ), $parsed_qs );
+        if ( ! empty( $parsed_qs ) && is_array( $parsed_qs ) ) {
+            $clean_qs = array();
+            foreach ( $parsed_qs as $qs_key => $qs_val ) {
+                $saf_key = sanitize_key( wp_unslash( (string) $qs_key ) );
+                if ( '' === $saf_key ) {
+                    continue;
+                }
+                if ( is_array( $qs_val ) ) {
+                    continue;
+                }
+                $clean_qs[ $saf_key ] = sanitize_text_field( wp_unslash( (string) $qs_val ) );
+            }
+            if ( ! empty( $clean_qs ) ) {
+                $redirect_link = add_query_arg( $clean_qs, $redirect_link );
+            }
+        }
+    }
+
+    return $redirect_link;
+}

Exploit Outline

To exploit this vulnerability, an attacker must first authenticate with a Subscriber account. They then navigate to the plugin's ad purchase page (typically `/buy-adspace`) to extract a valid 'security' nonce generated for the user session. With this nonce, the attacker crafts a GET request to the site root containing parameters used by the payment gateway callback logic: 'status=success', 'security' (the nonce), 'ad_slot_id' (a valid ad slot ID), 'refId' (the target order ID from the database), and 'user_id' (the attacker's own user ID). When the request is processed, the plugin updates the 'payment_status' of the order to 'paid' in the 'quads_adbuy_data' table, effectively bypassing the actual payment requirement.

Check if your site is affected.

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