CVE-2026-39656

Razorpay for WooCommerce <= 4.8.2 - Missing Authorization

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

Description

The Razorpay for WooCommerce plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 4.8.2. This makes it possible for unauthenticated attackers to perform an unauthorized action.

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.8.2
PublishedFebruary 16, 2026
Last updatedApril 15, 2026
Affected pluginwoo-razorpay
Research Plan
Unverified

Since the source files for version 4.8.2 of the **Razorpay for WooCommerce** plugin are not provided, this research plan is based on the vulnerability description (Missing Authorization) and common architectural patterns in this specific plugin. ### 1. Vulnerability Summary The **Razorpay for WooCo…

Show full research plan

Since the source files for version 4.8.2 of the Razorpay for WooCommerce plugin are not provided, this research plan is based on the vulnerability description (Missing Authorization) and common architectural patterns in this specific plugin.

1. Vulnerability Summary

The Razorpay for WooCommerce plugin (up to 4.8.2) suffers from a missing authorization vulnerability. This typically occurs when a sensitive function is registered as an AJAX handler (via wp_ajax_ or wp_ajax_nopriv_) or hooked into a global initialization hook (like admin_init or init), but fails to perform a current_user_can() capability check before executing its logic.

The vulnerability allows an unauthenticated attacker to trigger a function that might modify plugin settings, leak sensitive configuration data (like Razorpay API keys), or interfere with the order/payment lifecycle.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (for AJAX-based issues) or / (if hooked to init/admin_init).
  • Vulnerable Action (Inferred): Based on common issues in payment gateways, the likely candidates are:
    1. razorpay_update_settings (Updating API keys)
    2. razorpay_test_connection (Validating keys, potentially leaking info)
    3. razorpay_capture_payment (Manually triggering payment capture)
  • Authentication: None (Unauthenticated).
  • Payload Type: application/x-www-form-urlencoded.

3. Code Flow (Inferred Trace)

  1. Entry Point: An unauthenticated HTTP POST request is sent to admin-ajax.php with an action parameter.
  2. Hook Registration: The plugin registers a handler in its main class or an admin-specific file:
    // Potential registration in class-wc-razorpay.php or similar
    add_action( 'wp_ajax_nopriv_rzp_some_sensitive_action', array( $this, 'vulnerable_function' ) );
    add_action( 'wp_ajax_rzp_some_sensitive_action', array( $this, 'vulnerable_function' ) );
    
  3. Vulnerable Sink: The vulnerable_function executes. It may check for a WordPress nonce, but fails to check for user capabilities (e.g., manage_options).
  4. Execution: The function performs an action like update_option() or wp_remote_post() using attacker-supplied data.

4. Nonce Acquisition Strategy

If the vulnerable endpoint requires a nonce, it is likely exposed on the WooCommerce checkout page or the WordPress admin dashboard (if the vulnerability is meant for authenticated users but lacks capability checks).

  1. Identify Shortcode/Page: Razorpay usually loads on the Checkout page.
  2. Creation:
    wp post create --post_type=page --post_status=publish --post_title="Checkout" --post_content='[woocommerce_checkout]'
  3. Navigation: Use browser_navigate to visit the newly created checkout page.
  4. Extraction:
    Search for localized script data in the browser console.
    // Examples of what to look for:
    browser_eval("window.wc_razorpay_params?.nonce")
    browser_eval("window.razorpay_settings?.ajax_nonce")
    
  5. Bypass Check: If the code uses check_ajax_referer( 'action', 'nonce', false ) (with the false parameter) and doesn't check the return value, the nonce can be any string.

5. Exploitation Strategy

The goal is to identify which AJAX action is unprotected.

Step 1: Discovery
Find all registered AJAX actions in the plugin:

grep -rn "wp_ajax" /var/www/html/wp-content/plugins/woo-razorpay/

Check if any sensitive actions are registered with wp_ajax_nopriv_.

Step 2: Targeted Attack (Example: API Key Modification)
If an action like rzp_update_options is found without authorization checks:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=rzp_update_options&key_id=OWNED_KEY&key_secret=OWNED_SECRET&nonce=[EXTRACTED_NONCE]
    

Step 3: Verification
Confirm the settings were changed via WP-CLI.

6. Test Data Setup

  1. Install WooCommerce: The plugin requires WooCommerce to be active.
  2. Enable Razorpay: Configure a dummy set of Razorpay keys in the WooCommerce settings so the plugin is "active."
    wp option update woocommerce_razorpay_settings '{"enabled":"yes","key_id":"rzp_test_123","key_secret":"secret_123"}' --format=json
    
  3. Create Checkout Page: As described in Section 4.

7. Expected Results

  • Successful Exploit: The server responds with 200 OK or a success JSON message (e.g., {"success":true}).
  • State Change: The plugin's configuration options are modified to the attacker's values, or a sensitive action (like an order update) is performed.

8. Verification Steps

After sending the HTTP request, verify the impact using WP-CLI:

# Check if API keys were changed
wp option get woocommerce_razorpay_settings

# Check if a specific order status was changed
wp post get [ORDER_ID] --field=post_status

9. Alternative Approaches

If the vulnerability is not in an AJAX handler:

  1. Search for admin_init hooks:
    grep -rn "add_action.*admin_init" /var/www/html/wp-content/plugins/woo-razorpay/
    
    Examine the callback functions. If they perform actions based on $_GET or $_POST without current_user_can(), they are vulnerable. Note that admin_init runs for unauthenticated requests to /wp-admin/admin-ajax.php.
  2. Check REST API:
    grep -rn "register_rest_route" /var/www/html/wp-content/plugins/woo-razorpay/
    
    Look for routes where the permission_callback returns __return_true or is missing.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Razorpay for WooCommerce plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on AJAX handlers or administrative functions in versions up to and including 4.8.2. This vulnerability allows unauthenticated attackers to execute sensitive actions, such as modifying plugin configurations or API credentials, by directly interacting with unprotected endpoints.

Vulnerable Code

// Likely in includes/admin/class-wc-razorpay-admin.php or the main plugin file
// Sensitive actions are registered without sufficient capability checks

add_action( 'wp_ajax_nopriv_rzp_update_options', array( $this, 'vulnerable_function' ) );
add_action( 'wp_ajax_rzp_update_options', array( $this, 'vulnerable_function' ) );

---

// The callback function performs sensitive logic without verifying the user's role
public function vulnerable_function() {
    // The function may check a nonce but fails to check current_user_can()
    check_ajax_referer( 'rzp_update_nonce', 'security' );

    if ( isset( $_POST['key_id'] ) && isset( $_POST['key_secret'] ) ) {
        update_option( 'woocommerce_razorpay_settings', array(
            'key_id'     => sanitize_text_field( $_POST['key_id'] ),
            'key_secret' => sanitize_text_field( $_POST['key_secret'] ),
        ));
        wp_send_json_success();
    }
}

Security Fix

--- a/includes/admin/class-wc-razorpay-admin.php
+++ b/includes/admin/class-wc-razorpay-admin.php
@@ -10,6 +10,11 @@
 
 public function vulnerable_function() {
+    // Add capability check to ensure only authorized admins can modify settings
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => 'Forbidden' ), 403 );
+    }
+
     check_ajax_referer( 'rzp_update_nonce', 'security' );
 
     if ( isset( $_POST['key_id'] ) ) {

Exploit Outline

The exploit targets the WordPress AJAX endpoint (/wp-admin/admin-ajax.php). An unauthenticated attacker identifies a sensitive action registered via the 'wp_ajax_nopriv_' hook (or a 'wp_ajax_' hook that lacks capability checks). By sending a POST request with the 'action' parameter set to the vulnerable callback (e.g., 'rzp_update_options'), the attacker can trigger the function. If a nonce is required, it is typically retrieved by inspecting the checkout page's localized JavaScript variables (e.g., 'wc_razorpay_params'). The attacker then supplies a payload containing new API keys or configuration settings, which the plugin saves to the database because it fails to verify if the requester has administrative privileges.

Check if your site is affected.

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