CVE-2026-24560

Cloudinary <= 3.3.0 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Cloudinary plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.3.0. 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<=3.3.0
PublishedJanuary 22, 2026
Last updatedJanuary 27, 2026
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-24560 (Cloudinary Missing Authorization) ## 1. Vulnerability Summary The Cloudinary plugin for WordPress (versions <= 3.3.0) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, certain administrative functions registered v…

Show full research plan

Exploitation Research Plan - CVE-2026-24560 (Cloudinary Missing Authorization)

1. Vulnerability Summary

The Cloudinary plugin for WordPress (versions <= 3.3.0) contains a missing authorization vulnerability in its AJAX handling logic. Specifically, certain administrative functions registered via wp_ajax_* hooks fail to verify the caller's capabilities using current_user_can(). While these functions may implement CSRF protection via WordPress nonces, the nonces are often exposed to all authenticated users (including Subscribers) in the admin dashboard or post editor. This allows a Subscriber-level attacker to perform unauthorized administrative actions, such as modifying plugin settings or triggering server-side media synchronization tasks.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: cloudinary_save_settings (inferred) or cloudinary_sync_media (inferred).
  • Payload Parameter: settings (array), action, and nonce.
  • Authentication: Authenticated (Subscriber-level or higher).
  • Preconditions: The plugin must be active, and the attacker must be able to retrieve a valid nonce.

3. Code Flow (Inferred)

  1. Registration: The plugin registers AJAX handlers in a class (likely Cloudinary\Admin\Ajax or similar) using:
    add_action( 'wp_ajax_cloudinary_save_settings', [ $this, 'save_settings' ] );
  2. Entry Point: An authenticated user sends a POST request to admin-ajax.php with action=cloudinary_save_settings.
  3. Vulnerable Callback: The save_settings function is invoked.
  4. Missing Check:
    • It likely calls check_ajax_referer( 'cloudinary_nonce', 'nonce' ); which validates the CSRF token.
    • It fails to call if ( ! current_user_can( 'manage_options' ) ) wp_die();.
  5. Sink: The function proceeds to update the plugin settings using update_option( 'cloudinary_settings', ... );.

4. Nonce Acquisition Strategy

The Cloudinary plugin typically localizes administrative parameters for its JavaScript components. These are often enqueued in the post editor or the media library, which are accessible to Subscribers/Contributors.

  • Target Variable: window.cloudinary_config or window.cloudinary_params (inferred).
  • Nonce Key: nonce or cloudinary_nonce (inferred).
  • Extraction Method:
    1. Create a standard post as a Subscriber.
    2. The Cloudinary "Add Media" button or library integration enqueues scripts.
    3. Use browser_eval to extract the nonce from the global JS object.

Steps:

  1. Login as a Subscriber.
  2. Navigate to /wp-admin/post-new.php.
  3. Execute: browser_eval("window.cloudinary_params?.nonce || window.cloudinary_config?.nonce").

5. Exploitation Strategy

We will attempt to modify the Cloudinary cloud_name setting. Changing this can hijack media delivery or cause a Denial of Service by pointing the site to a non-existent or attacker-controlled Cloudinary account.

  • Request Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=cloudinary_save_settings&nonce=[EXTRACTED_NONCE]&settings[cloud_name]=pwned_cloud&settings[api_key]=123456789&settings[api_secret]=abcdef
    
    (Note: The exact structure of the settings array may vary; if the above fails, we will try flat parameters like cloud_name=pwned_cloud).

6. Test Data Setup

  1. Plugin Setup: Install and activate Cloudinary <= 3.3.0. Configure it with dummy API credentials.
  2. User Creation: Create a user with the subscriber role.
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Content: Ensure at least one post exists to ensure the editor context is available for nonce extraction.

7. Expected Results

  • Successful Exploitation: The server returns a 200 OK or a JSON success response (e.g., {"success":true}).
  • Database Impact: The WordPress option cloudinary_settings (or equivalent) is updated with the attacker's values.
  • UI Impact: The Cloudinary settings page in the admin dashboard shows the changed cloud_name.

8. Verification Steps

After the HTTP request, verify the change using WP-CLI:

# Check the settings option
wp option get cloudinary_settings

# Verify the specific key inside the serialized array (if applicable)
wp eval 'echo get_option("cloudinary_settings")["cloud_name"];'

9. Alternative Approaches

If cloudinary_save_settings is not the correct action:

  1. Action Discovery: Search the plugin directory for all wp_ajax_ hooks:
    grep -r "wp_ajax_" wp-content/plugins/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/
  2. Target Metadata Sync: If settings are protected, try triggering an unauthorized sync:
    action=cloudinary_sync_media&nonce=[NONCE]&path=/
  3. Check for "Global" Nonces: If no Cloudinary-specific nonce is found, check if the plugin mistakenly uses the default REST nonce or an admin-ajax nonce registered by other parts of the system.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Cloudinary plugin for WordPress (up to 3.3.0) fails to perform authorization checks on several administrative AJAX handlers, relying solely on nonce validation. This allows authenticated users with Subscriber-level access to retrieve a nonce from the admin interface and perform unauthorized actions, such as modifying plugin settings or triggering media synchronization tasks.

Vulnerable Code

// Inferred registration of AJAX handler in versions <= 3.3.0
add_action( 'wp_ajax_cloudinary_save_settings', [ $this, 'save_settings' ] );

// Inferred vulnerable callback in versions <= 3.3.0
public function save_settings() {
    check_ajax_referer( 'cloudinary_nonce', 'nonce' );

    // Vulnerability: Missing current_user_can('manage_options') check before processing administrative changes
    $settings = $_POST['settings'];
    update_option( 'cloudinary_settings', $settings );
    wp_send_json_success();
}

Security Fix

--- a/php/class-cloudinary-admin-ajax.php
+++ b/php/class-cloudinary-admin-ajax.php
@@ -10,6 +10,10 @@
 	public function save_settings() {
 		check_ajax_referer( 'cloudinary_nonce', 'nonce' );
+
+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.' ) );
+		}
+
 		$settings = isset( $_POST['settings'] ) ? $_POST['settings'] : [];
 		update_option( 'cloudinary_settings', $settings );
 		wp_send_json_success();

Exploit Outline

1. Login to the WordPress site as a user with Subscriber-level permissions. 2. Navigate to an admin page where the plugin enqueues scripts, such as the post editor (wp-admin/post-new.php). 3. Extract the 'cloudinary_nonce' value from the localized JavaScript configuration, typically found within 'window.cloudinary_params' or 'window.cloudinary_config'. 4. Construct a POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'cloudinary_save_settings' and the 'nonce' parameter set to the extracted value. 5. Include a 'settings' array in the payload containing malicious values (e.g., setting 'cloud_name' to an attacker-controlled account or changing API keys). 6. Send the request and verify that the plugin settings are updated in the database without authorization.

Check if your site is affected.

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