CVE-2026-25325

rtMedia for WordPress, BuddyPress and bbPress <= 4.7.8 - Unauthenticated Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
4.7.9
Patched in
93d
Time to patch

Description

The rtMedia for WordPress, BuddyPress and bbPress plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 4.7.8. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=4.7.8
PublishedFebruary 1, 2026
Last updatedMay 4, 2026
Affected pluginbuddypress-media

What Changed in the Fix

Changes introduced in v4.7.9

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

### 1. Vulnerability Summary The **rtMedia for WordPress, BuddyPress and bbPress** plugin (versions <= 4.7.8) contains an unauthenticated information exposure vulnerability. The plugin fails to properly restrict access to certain REST API endpoints or frontend localized variables that contain sensit…

Show full research plan

1. Vulnerability Summary

The rtMedia for WordPress, BuddyPress and bbPress plugin (versions <= 4.7.8) contains an unauthenticated information exposure vulnerability. The plugin fails to properly restrict access to certain REST API endpoints or frontend localized variables that contain sensitive configuration data. Specifically, site-wide options (including third-party API keys, license information, and server paths) or private media metadata can be extracted by an unauthenticated attacker.

The vulnerability stems from inadequate permission checks in the RTMediaRestAPI (registered via app/main/controllers/api/RTMediaRestAPI.php) and the excessive exposure of the global $rtmedia->options array in frontend JavaScript localization.

2. Attack Vector Analysis

  • Endpoints:
    1. REST API: /wp-json/rtmedia/v1/settings or /wp-json/rtmedia/v1/support.
    2. Frontend: Any public-facing page where rtMedia scripts are enqueued (e.g., activity feed, media gallery pages).
  • Authentication: None required (Unauthenticated).
  • Parameter: rtmedia_config (JavaScript variable) or direct GET requests to REST routes.
  • Preconditions: The plugin must be active. For JavaScript-based leaks, a page with an rtMedia shortcode (e.g., [rtmedia_gallery]) or the BuddyPress Activity page must be publicly accessible.

3. Code Flow

  1. Initialization: In index.php, the global $rtmedia object is instantiated ($rtmedia = new RTMedia();).
  2. Option Loading: In app/admin/RTMediaUploadTermsAdmin.php (and other components), the plugin frequently loads the entire rtmedia-options site option:
    $rtmedia->options = rtmedia_get_site_option( 'rtmedia-options' );
    
  3. Localization: The plugin enqueues the main frontend script (rtmedia-main) and localizes configuration data for use in the browser. In the RTMedia main class, wp_localize_script is called to pass values to rtmedia_config. If this call includes the options array without filtering sensitive keys, it is exposed in the HTML source.
  4. REST API: The RTMediaRestAPI class registers routes during rest_api_init. If endpoints designed for debugging or settings do not implement a permission_callback checking for manage_options, any unauthenticated user can query them.

4. Nonce Acquisition Strategy

While many REST endpoints in rtMedia require a nonce for modifying data, many information exposure vulnerabilities in this plugin involve GET requests that either omit the nonce check or use the standard wp_rest nonce which is often publicly exposed.

To obtain the rtmedia_config data (including nonces if present):

  1. Identify Page: Identify a page where rtMedia is active. If BuddyPress is installed, the /activity/ page is the primary target.
  2. Create Test Page: If no public page exists, create one with the gallery shortcode:
    wp post create --post_type=page --post_title="Media" --post_status=publish --post_content='[rtmedia_gallery]'
    
  3. Extract Variable: Use browser_navigate to the page and browser_eval to extract the configuration object:
    // Target the main rtMedia config object
    browser_eval("window.rtmedia_config")
    

5. Exploitation Strategy

The goal is to extract the rtmedia-options array, which may contain sensitive keys.

Step 1: Probe REST API (Settings/Support)
Check for unrestricted access to configuration endpoints.

  • Request: GET /wp-json/rtmedia/v1/support
  • Alternative Request: GET /wp-json/rtmedia/v1/settings
  • Tool: http_request

Step 2: Extract Frontend Configuration
If REST is restricted, extract the data via the localized script variable.

  • Action: Navigate to the homepage or a page with [rtmedia_gallery].
  • Tool: browser_navigate followed by browser_eval("window.rtmedia_config").
  • Target Data: Look for rtmedia_config.options or rtmedia_config.api_config.

Step 3: Check for Media Metadata Leak
Check if private media metadata (ID, title, owner) can be listed without authentication.

  • Request: GET /wp-json/rtmedia/v1/media?per_page=10
  • Tool: http_request

6. Test Data Setup

  1. Install rtMedia and BuddyPress: Ensure the plugin is active.
  2. Populate Dummy Sensitive Data: Add a fake S3 key or transcoding secret to the options:
    wp option patch update rtmedia-options s3_secret_key "EXPLOIT_SUCCESS_S3_KEY"
    wp option patch update rtmedia-options transcoding_api_key "EXPLOIT_SUCCESS_TRANSCODING_KEY"
    
  3. Create Private Media: Upload a media item and set its privacy to 60 (Private).
    # Note: Requires a user ID, assuming 1
    wp eval "rtmedia_db_insert(array('media_id' => 123, 'privacy' => 60, 'media_title' => 'SENSITIVE_LEAK_TITLE', 'media_type' => 'photo'));"
    

7. Expected Results

  • REST API: The response should return a JSON object containing the rtmedia-options or system debug information.
  • JS Extraction: The rtmedia_config object in the console should contain the s3_secret_key or other configuration parameters.
  • Media Leak: The response from /wp-json/rtmedia/v1/media should include the entry with the title SENSITIVE_LEAK_TITLE despite it being private.

8. Verification Steps

  1. Verify S3 Key Exposure:
    # Run after HTTP request
    # If the response contains "EXPLOIT_SUCCESS_S3_KEY", vulnerability is confirmed.
    
  2. Check User Exposure: Verify if the leaked REST API data contains user emails or internal file paths (e.g., ABSPATH).

9. Alternative Approaches

If /wp-json/rtmedia/v1/ is blocked, try:

  1. AJAX Endpoint: POST /wp-admin/admin-ajax.php with action=rtmedia_get_support_data.
  2. Shortcode-based Exposure: Some shortcodes render attributes directly into the page source without escaping. Check [rtmedia_gallery] output for hidden attributes or metadata in JSON blobs.
  3. **Transcoding
Research Findings
Static analysis — not yet PoC-verified

Summary

The rtMedia plugin for WordPress (<= 4.7.8) is vulnerable to unauthenticated information exposure through an AJAX endpoint in its GoDAM integration. The plugin registered a public AJAX action that lacked both authentication and proper permission checks, allowing unauthorized users to retrieve and view private BuddyPress activity comments.

Vulnerable Code

// templates/media/godam-integration.php

	/**
	 * Handle AJAX request for loading a single activity comment's HTML.
	 */
	add_action( 'wp_ajax_get_single_activity_comment_html', 'handle_get_single_activity_comment_html' );
	add_action( 'wp_ajax_nopriv_get_single_activity_comment_html', 'handle_get_single_activity_comment_html' ); // Line 187

	/**
	 * AJAX handler to fetch and return the HTML for a single activity comment.
	 *
	 * Validates the request, loads the activity comment by ID,
	 * renders its HTML using the BuddyPress template, and returns it in a JSON response.
	 *
	 * @return void Outputs JSON response with rendered HTML or error message.
	 */
	function handle_get_single_activity_comment_html() { // Line 198
		check_ajax_referer( 'godam-ajax-nonce', 'nonce' );

		$activity_id = isset( $_POST['comment_id'] ) ? intval( $_POST['comment_id'] ) : 0;

		if ( ! $activity_id ) {
			wp_send_json_error( 'Invalid activity ID' );
		}

		$activity = new BP_Activity_Activity( $activity_id );
		if ( empty( $activity->id ) ) {
			wp_send_json_error( 'Activity comment not found' );
		}

		global $activities_template;
        // ... (truncated)

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.8/app/admin/RTMediaUploadTermsAdmin.php /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.9/app/admin/RTMediaUploadTermsAdmin.php
--- /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.8/app/admin/RTMediaUploadTermsAdmin.php	2025-10-30 08:47:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.9/app/admin/RTMediaUploadTermsAdmin.php	2026-01-30 10:11:50.000000000 +0000
@@ -37,8 +37,7 @@
 		 * Constructing settings for upload terms.
 		 */
 		public function __construct() {
-			$this->upload_terms_message       = esc_html__( 'terms of services.', 'buddypress-media' );
-			$this->upload_terms_error_message = esc_html__( 'Please check terms of service.', 'buddypress-media' );
+			add_action( 'init', array( $this, 'init_translations' ), 5 );
 
 			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts_styles' ), 999 );
 			add_filter( 'rtmedia_general_content_default_values', array( $this, 'add_admin_option_default_value' ), 10, 1 );
@@ -47,6 +46,14 @@
 		}
 
 		/**
+		 * Initialize translations at the proper time.
+		 */
+		public function init_translations() {
+			$this->upload_terms_message       = esc_html__( 'terms of services.', 'buddypress-media' );
+			$this->upload_terms_error_message = esc_html__( 'Please check terms of service.', 'buddypress-media' );
+		}
+
+		/**
 		 * Loads styles and scripts
 		 *
 		 * @global object $rtmedia
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.8/templates/media/godam-integration.php /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.9/templates/media/godam-integration.php
--- /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.8/templates/media/godam-integration.php	2025-09-02 11:04:34.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/buddypress-media/4.7.9/templates/media/godam-integration.php	2026-01-30 10:11:50.000000000 +0000
@@ -183,14 +186,15 @@
 
 	/**
 	 * Handle AJAX request for loading a single activity comment's HTML.
+	 * Note: Only registered for logged-in users (wp_ajax_) for security.
 	 */
 	add_action( 'wp_ajax_get_single_activity_comment_html', 'handle_get_single_activity_comment_html' );
-	add_action( 'wp_ajax_nopriv_get_single_activity_comment_html', 'handle_get_single_activity_comment_html' );
 
 	/**
 	 * AJAX handler to fetch and return the HTML for a single activity comment.
 	 *
 	 * Validates the request, loads the activity comment by ID,
+	 * verifies the user has permission to view it,
 	 * renders its HTML using the BuddyPress template, and returns it in a JSON response.
 	 *
 	 * @return void Outputs JSON response with rendered HTML or error message.
@@ -198,15 +202,25 @@
 	function handle_get_single_activity_comment_html() {
 		check_ajax_referer( 'godam-ajax-nonce', 'nonce' );
 
+		// Require user to be logged in.
+		if ( ! is_user_logged_in() ) {
+			wp_send_json_error( __( 'Authentication required', 'buddypress-media' ), 401 );
+		}
+
 		$activity_id = isset( $_POST['comment_id'] ) ? intval( $_POST['comment_id'] ) : 0;
 
 		if ( ! $activity_id ) {
-			wp_send_json_error( 'Invalid activity ID' );
+			wp_send_json_error( __( 'Invalid activity ID', 'buddypress-media' ), 400 );
 		}
 
 		$activity = new BP_Activity_Activity( $activity_id );
 		if ( empty( $activity->id ) ) {
-			wp_send_json_error( 'Activity comment not found' );
+			wp_send_json_error( __( 'Activity comment not found', 'buddypress-media' ), 404 );
+		}
+
+		// Verify user has permission to view this activity.
+		if ( ! rtmedia_user_can_view_activity( $activity ) ) {
+			wp_send_json_error( __( 'You do not have permission to view this activity', 'buddypress-media' ), 403 );
 		}
 
 		global $activities_template;

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker first identifies a valid `godam-ajax-nonce` by inspecting the page source or network traffic of any page where rtMedia's GoDAM integration scripts are enqueued. The attacker then sends an unauthenticated POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `get_single_activity_comment_html`, the `nonce` parameter set to the intercepted value, and a `comment_id` parameter targeting a specific BuddyPress activity comment. Because the `nopriv` action is registered and the handler fails to perform authentication or group/profile privacy checks, the server returns the full rendered HTML content of the private comment in the JSON response.

Check if your site is affected.

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