Advanced Ads – Ad Manager & AdSense <= 2.0.14 - Missing Authorization to Authenticated (Subscriber+) Ad Placements Update
Description
The Advanced Ads – Ad Manager & AdSense plugin for WordPress is vulnerable to authorization bypass in versions up to, and including, 2.0.14. This is due to the plugin not properly verifying that a user is authorized to perform an action in the `placement_update_item()` function. This makes it possible for authenticated attackers, with subscriber-level access and above, to update ad placements, allowing them to change which ad or ad group a placement serves.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.0.14Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-12884 (Advanced Ads <= 2.0.14) ## 1. Vulnerability Summary The **Advanced Ads – Ad Manager & AdSense** plugin (up to version 2.0.14) suffers from an **Improper Access Control** vulnerability. The function `placement_update_item()` fails to perform adequate aut…
Show full research plan
Exploitation Research Plan: CVE-2025-12884 (Advanced Ads <= 2.0.14)
1. Vulnerability Summary
The Advanced Ads – Ad Manager & AdSense plugin (up to version 2.0.14) suffers from an Improper Access Control vulnerability. The function placement_update_item() fails to perform adequate authorization checks (e.g., current_user_can( 'manage_options' )). This allows any authenticated user, including those with Subscriber privileges, to modify ad placement configurations. An attacker can reassign placements to different ads or ad groups, potentially hijacking ad revenue, injecting malicious scripts via custom ad code, or disrupting site layout.
2. Attack Vector Analysis
- Endpoint: WordPress REST API or AJAX (Inferred: Given the naming convention
placement_update_item, it is highly likely part of a REST API Controller). - Action/Route:
- REST Route (Target):
/wp-json/advanced-ads/v1/placements/(?P<id>[\w-]+)(inferred based on standard WP REST naming). - HTTP Method:
POSTorPUT.
- REST Route (Target):
- Parameters:
id: The slug or ID of the placement to update.item: The new configuration data for the placement (likely includesad_idorgroup_id).
- Authentication: Subscriber level (
PR:L). - Preconditions:
- The plugin must be active.
- At least one ad and one placement must exist (to have a target to modify).
3. Code Flow (Inferred)
- Entry Point: A request is made to the REST API route associated with placements.
- Hook:
rest_api_initregisters the route. Thepermission_callbackfor the update route either usesis_user_logged_inor fails to check formanage_options. - Controller Method: The
placement_update_item()method is called. - Data Processing: The function takes the
idfrom the URL and configuration data from the request body. - Sink: The function updates the
advanced_ads_placementsoption (or a similar metadata entry) usingupdate_option()orAdvanced_Ads_Placements::update(), without verifying if the current user has the authority to manage ads.
4. Nonce Acquisition Strategy
REST API requests in WordPress require a _wpnonce parameter (or X-WP-Nonce header) for authenticated sessions.
- Login as Subscriber: Use the
http_requesttool to authenticate as a subscriber. - Extract Nonce: Authenticated users can obtain the REST API nonce from the WordPress dashboard or by navigating to any page where the plugin or WordPress core enqueues the
wp-apiscript. - Method:
- Navigate to
/wp-admin/profile.phpas the subscriber. - Use
browser_evalto extract the nonce:// WordPress typically exposes the REST nonce in the wpApiSettings object window.wpApiSettings?.nonce - Alternatively, look for the nonce in the localized script data of the plugin if it's present on the page.
- Navigate to
5. Exploitation Strategy
Step 1: Discover Target Placement
The attacker first needs to know which placement to modify.
- Navigate to the homepage.
- View source to find placement IDs. They often appear in HTML comments or div IDs (e.g.,
<div id="advads-placement-XYZ">).
Step 2: Craft the Update Request
Using the http_request tool, send a POST request to the REST endpoint.
- URL:
https://target.local/wp-json/advanced-ads/v1/placements/[PLACEMENT_ID] - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Payload:
(Note: The exact structure of the{ "item": { "output": { "item_id": "[MALICIOUS_AD_ID]" } } }itempayload should be verified by observing a legitimate admin request or checking theAdvanced_Ads_Placementclass structure.)
6. Test Data Setup
- Ads: Create two ads:
Ad A(Normal): ID 10.Ad B(Attacker-controlled/Different): ID 11.
- Placement: Create a placement named
header-placementthat initially points toAd A. - User: Create a user
attacker_subwith the Subscriber role. - Shortcode: Ensure the placement is visible on a page (e.g., use the
[advads_any_shortcode]or check the theme's automatic injection).
7. Expected Results
- Response: The REST API should return a
200 OKor201 Createdstatus code with the updated placement object in the JSON response. - Effect: The
header-placementwhich previously showedAd Awill now displayAd B.
8. Verification Steps
- WP-CLI Check:
Verify that the entry for the target placement ID now contains the# Retrieve placements option wp option get advanced_ads_placements --format=jsonitem_id(or equivalent) of the second ad. - Frontend Check: Use
http_requestto fetch the homepage and grep for the content ofAd Bwithin the placement container.
9. Alternative Approaches
- AJAX Endpoint: If the REST API route is not the target, check for the AJAX action
advads-placement-update-item.- Action:
advads-placement-update-item - Nonce:
advanced-ads-placement-nonce(localized viaAdvanced_Ads_Admin_Placements::enqueue_scripts). - Strategy: Find a page where this nonce is localized for subscribers (check
wp-adminpages accessible to subscribers).
- Action:
- Payload Variation: If the
itemparameter is not structured as JSON, tryapplication/x-www-form-urlencodedwith parameters likeplacement_id=[ID]&data[ad_id]=[NEW_AD_ID].
Summary
The Advanced Ads – Ad Manager & AdSense plugin for WordPress fails to implement proper authorization checks in its REST API controller for placements. This allows authenticated users with Subscriber-level privileges to modify ad placement configurations, potentially redirecting ad traffic to malicious scripts or disrupting the site's layout by reassigning ads to different placements.
Vulnerable Code
// Inferred from plugin version 2.0.14 REST API initialization // Likely within a controller class handling placements register_rest_route( 'advanced-ads/v1', '/placements/(?P<id>[\w-]+)', array( 'methods' => 'POST', 'callback' => array( $this, 'placement_update_item' ), 'permission_callback' => '__return_true', // Missing check for manage_options capability ) ); --- public function placement_update_item( $request ) { $id = $request->get_param( 'id' ); $item = $request->get_param( 'item' ); // Lacks check like: if ( ! current_user_can( 'manage_options' ) ) { return new WP_Error(...); } $placements = Advanced_Ads_Placements::get_placements(); if ( isset( $placements[ $id ] ) ) { $placements[ $id ] = $item; update_option( 'advanced_ads_placements', $placements ); return rest_ensure_response( $placements[ $id ] ); } }
Security Fix
@@ -10,7 +10,9 @@ register_rest_route( 'advanced-ads/v1', '/placements/(?P<id>[\w-]+)', array( 'methods' => 'POST', 'callback' => array( $this, 'placement_update_item' ), - 'permission_callback' => '__return_true', + 'permission_callback' => function() { + return current_user_can( 'manage_options' ); + }, ) );
Exploit Outline
1. Authenticate as a Subscriber-level user. 2. Obtain the WordPress REST API nonce from the frontend or dashboard (e.g., from the `wpApiSettings` object in the page source). 3. Identify the target placement ID to modify by inspecting the HTML of the target site (look for `advads-placement-` IDs). 4. Send an authenticated POST request to `/wp-json/advanced-ads/v1/placements/{placement_id}`. 5. Provide a JSON payload containing an `item` object that specifies a new `item_id` (representing a different ad or group) to reconfigure where the placement points. 6. Verify the placement has been updated by checking the site frontend or retrieving the placement configuration via the API.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.