Quads Ads Manager for Google AdSense <= 3.0.2 - Missing Authorization
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:NTechnical Details
<=3.0.2What Changed in the Fix
Changes introduced in v3.0.3
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-42732 ## 1. Vulnerability Summary The **Quads Ads Manager for Google AdSense** plugin (up to version 3.0.2) contains a **Missing Authorization** vulnerability in its ad-selling module. Specifically, the function `quads_authorize_payment_success` is hooked to …
Show full research plan
Exploitation Research Plan - CVE-2026-42732
1. Vulnerability Summary
The Quads Ads Manager for Google AdSense plugin (up to version 3.0.2) contains a Missing Authorization vulnerability in its ad-selling module. Specifically, the function quads_authorize_payment_success is hooked to WordPress's init action, making it accessible on every page load. The function lacks proper authorization checks (e.g., current_user_can) and fails to verify if the request originated from a legitimate payment gateway. While it contains a check for is_user_logged_in() and a nonce check in the provided source, the CVE identifies it as unauthenticated, suggesting either these checks are bypassable, were absent in earlier versions, or the nonce itself is exposed to unauthenticated users.
Successful exploitation allows an attacker to mark pending ad orders as "paid" without actual payment, effectively stealing advertising space.
2. Attack Vector Analysis
- Endpoint: Any WordPress URL (via the
inithook). - Vulnerable Function:
quads_authorize_payment_success()inincludes/ad-selling-helper.php. - HTTP Method:
GET. - Payload Parameters:
status: Must be exactlysuccess.ad_slot_id: An integer representing the target ad slot ID.refId: An integer representing the order ID (intended to be mapped to$order_id).user_id: An integer representing the user ID of the ad buyer.security: A WordPress nonce for the action'security'.
- Preconditions:
- The "Sellable Ads" feature must be active.
- A pending order must exist in the
wp_quads_adbuy_datatable. - A valid nonce for the
'security'action must be obtained.
3. Code Flow
- Entry Point: A
GETrequest is made to any WordPress page. - Hook: The
inithook triggersquads_authorize_payment_successinincludes/ad-selling-helper.php. - Authentication Check: The function checks
is_user_logged_in(). (If the CVE is unauthenticated, this check might be absent in vulnerable versions or the attacker simply needs a Subscriber account). - Nonce Verification:
wp_verify_nonce( $_GET['security'], 'security' ). - Logic Path: If
status == 'success',ad_slot_id > 0,refId != "", anduser_id > 0, the code enters the update block. - Sink: The plugin performs a database update:
Note: In the provided source,$wpdb->update( $table_name, array('payment_status' => 'paid' , 'payment_response'=> wp_json_encode($params)), array('id' => $order_id , 'user_id'=>$user->ID) );$order_idand$user_idare used but not explicitly initialized from$_GETbefore theabsint()call. This is likely a developer error in the plugin's code that may rely onregister_globals(unlikely) or an earlierextract($_GET)(possible).
4. Nonce Acquisition Strategy
The nonce is tied to the action 'security'. In this plugin, this generic nonce is typically localized for the frontend ad-buying form.
- Identify Page: The plugin creates a page with the slug
buy-adspacecontaining the shortcode[quads_buy_form]. - Navigate: Use the browser to navigate to
/buy-adspace/. - Extract: Use
browser_evalto find the localized JavaScript object. The plugin usually localizes variables under a name related to "quads".- Candidate Variable:
quads_ajax_objectorquads_vars. - Command:
browser_eval("window.quads_ajax_object?.security")orbrowser_eval("window.quads_vars?.security"). - Verification: If no specific variable is found, search the HTML source for
"security":".
- Candidate Variable:
5. Exploitation Strategy
- Setup Phase:
- Ensure the plugin is active and the
wp_quads_adbuy_datatable exists. - Create a "Sellable Ad" order in the database with
payment_status = 'pending'.
- Ensure the plugin is active and the
- Nonce Extraction:
- Access the
/buy-adspace/page and extract the'security'nonce.
- Access the
- Exploit Execution:
- Construct the malicious GET request.
- URL:
http://localhost:8080/?status=success&ad_slot_id=[SLOT_ID]&refId=[ORDER_ID]&user_id=[USER_ID]&security=[NONCE] - Note: If the source code in the target environment matches the provided snippet exactly, the undefined
$order_idmay cause the exploit to fail unless the agent can determine if the plugin usesrefIdas the order ID.
- Confirmation:
- Verify that the database record's
payment_statushas changed topaid.
- Verify that the database record's
6. Test Data Setup
# 1. Create a test ad slot (if applicable) or identify an existing one
# 2. Create the Buy Adspace page if it doesn't exist
wp post create --post_type=page --post_title="Buy Adspace" --post_status=publish --post_content='[quads_buy_form]' --post_name='buy-adspace'
# 3. Create a pending order in the custom table (requires identifying the table name)
# Assuming table: wp_quads_adbuy_data
# Columns: id, user_id, ad_id, payment_status, etc.
wp db query "INSERT INTO wp_quads_adbuy_data (user_id, ad_id, payment_status, start_date, end_date) VALUES (1, 123, 'pending', '2025-01-01', '2025-01-31');"
# Note the ID of the inserted row for refId
7. Expected Results
- The HTTP request should return a
200 OK(the function returns nothing or callswp_mail). - An email will be sent to the admin and user (can be checked via
wp mail listif a mail catcher is used). - The database entry in
wp_quads_adbuy_datafor the specificidwill havepayment_statusset to'paid'.
8. Verification Steps
# Check the database for the status change
wp db query "SELECT id, payment_status FROM wp_quads_adbuy_data WHERE id = [ORDER_ID];"
# Verify the output matches 'paid'
9. Alternative Approaches
- Variable Context: If the GET parameters
refIdanduser_iddo not populate the internal$order_idand$user_idvariables, try adding them directly asorder_id=[ID]anduser_id=[ID]in the query string, as some plugins useextract($_GET)or$_REQUEST. - Subscriber Level: If the
is_user_logged_in()check is strictly enforced, create a Subscriber user, log in, and then perform the request. The vulnerability is still "Missing Authorization" because a Subscriber should not be able to authorize payments for ad slots.
Summary
The Quads Ads Manager plugin for WordPress is vulnerable to unauthorized access via the quads_authorize_payment_success function. This function is hooked to 'init' and lacks proper authorization or capability checks, allowing authenticated attackers (like Subscribers) to mark pending ad orders as paid without completing actual payments.
Vulnerable Code
// includes/ad-selling-helper.php 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'] )){ $slot_id = absint( $_GET['ad_slot_id'] ); $order_id = absint($order_id); $user_id = absint($user_id); $price = get_post_meta( $slot_id, 'ad_cost' ); --- $params = array(); $params['payment_date'] = gmdate('Y-m-d H:i:s'); // 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
@@ -1710,6 +1838,21 @@ } } + $price = get_post_meta( $ad_slot_id, 'ad_cost', true ); + $currency = 'USD'; + $days = ( strtotime( $end_date ) - strtotime( $start_date ) ) / ( 60 * 60 * 24 ) + 1; + $total_cost = $price * $days; + $name = get_the_title( $ad_slot_id ); + + $coupon_parse = quads_parse_coupon_discount( $coupon_code, $ad_slot_id, (float) $total_cost ); + if ( 'invalid' === $coupon_parse['status'] || 'expired' === $coupon_parse['status'] ) { + $err_msg = ( 'expired' === $coupon_parse['status'] ) + ? esc_html__( 'Coupon expired, please try another one.', 'quick-adsense-reloaded' ) + : esc_html__( 'Invalid coupon, please try another one.', 'quick-adsense-reloaded ); + wp_send_json_error( array( 'message' => $err_msg ) ); + } + $total_cost = max( 0, (float) $total_cost - (float) $coupon_parse['discount'] ); + // Insert the ad buy record in the database global $wpdb; $table_name = $wpdb->prefix . 'quads_adbuy_data';
Exploit Outline
1. Authentication: The attacker authenticates as a low-privileged user (e.g., Subscriber). 2. Nonce Acquisition: The attacker visits the plugin's ad-selling page (typically /buy-adspace/) to extract a valid WordPress nonce for the action 'security'. This nonce is often localized in JavaScript variables like 'quads_ajax_object'. 3. Order Identification: The attacker initiates an ad purchase or identifies an existing pending order ID (refId) and their own user_id. 4. Request Construction: The attacker constructs a GET request to the site root with parameters mimicking a successful payment gateway callback: ?status=success&ad_slot_id=[SLOT_ID]&refId=[ORDER_ID]&user_id=[USER_ID]&security=[NONCE]. 5. Execution: Upon receiving the request, the init hook triggers quads_authorize_payment_success, which validates the nonce and user status but fails to verify that the user has administrative privileges or that the payment was verified by the provider. The plugin then updates the database to set the order status to 'paid'.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.