Cost Calculator Builder <= 3.6.9 - Missing Authorization to Unauthenticated Payment Status Bypass
Description
The Cost Calculator Builder plugin for WordPress is vulnerable to Unauthenticated Payment Status Bypass in all versions up to, and including, 3.6.9 only when used in combination with Cost Calculator Builder PRO. This is due to the complete_payment AJAX action being registered via wp_ajax_nopriv, making it accessible to unauthenticated users, and the complete() function only verifying a nonce without checking user capabilities or order ownership. Since nonces are exposed to all visitors via window.ccb_nonces in the page source, any unauthenticated attacker can mark any order's payment status as "completed" without actual payment.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=3.6.9Source Code
WordPress.org SVNThis research plan outlines the methodology for exploiting CVE-2025-14757, a Missing Authorization vulnerability in the Cost Calculator Builder plugin. ### 1. Vulnerability Summary The Cost Calculator Builder plugin (specifically when paired with the PRO version) registers an AJAX action `complete_…
Show full research plan
This research plan outlines the methodology for exploiting CVE-2025-14757, a Missing Authorization vulnerability in the Cost Calculator Builder plugin.
1. Vulnerability Summary
The Cost Calculator Builder plugin (specifically when paired with the PRO version) registers an AJAX action complete_payment via wp_ajax_nopriv_complete_payment, making it accessible to unauthenticated users. The handler function for this action (likely complete() in a payment-related class) verifies a WordPress nonce but fails to perform any capability checks or verify if the requester has ownership of the order. Because the required nonce is globally exposed in the frontend source code via the ccb_nonces JavaScript object, any visitor can mark any order as "completed" without performing an actual payment.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
complete_payment - HTTP Method: POST
- Authentication: None (Unauthenticated)
- Vulnerable Parameter:
order_id(inferred) orid(inferred) - Nonce Parameter:
nonce(referenced in the description) - Preconditions:
- Cost Calculator Builder (Base) <= 3.6.9 installed.
- Cost Calculator Builder PRO installed and active.
- At least one order must exist in the system (e.g., in "pending" status).
3. Code Flow (Inferred)
- Entry: An unauthenticated POST request is sent to
admin-ajax.php?action=complete_payment. - Hook: WordPress matches the
wp_ajax_nopriv_complete_paymenthook to a handler function (likely in anOrdersorPaymentcontroller). - Nonce Check: The handler calls
check_ajax_referer( 'ccb_nonce', 'nonce' )(action string inferred). This passes because the nonce is publicly available. - Authorization Failure: The code proceeds to update the order status without calling
current_user_can()or checking if the order belongs to the current session/user. - State Change: The handler calls a database update (likely via
$wpdb) to change the order's status column tocompletedorpaid.
4. Nonce Acquisition Strategy
The vulnerability description explicitly identifies the nonce source.
- Identify Page: Find a page containing a Cost Calculator (usually uses shortcode
[cost_calculator]or[cc_builder]). - Navigate: Use
browser_navigateto visit that page. - Extract: Use
browser_evalto extract the nonce from the localized script object.- JavaScript Command:
window.ccb_nonces?.ccb_nonce(or similar key within theccb_noncesobject). - Localization Key:
ccb_nonces(from source).
- JavaScript Command:
5. Exploitation Strategy
- Setup Environment: Ensure both CCB and CCB PRO are active.
- Create Target Order: Use the frontend calculator to submit a "booking" or "order" that results in a "Pending" status. Note the Order ID.
- Retrieve Nonce: Navigate to any page with the calculator and extract the nonce from
window.ccb_nonces. - Craft Exploit Request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=complete_payment&nonce=[EXTRACTED_NONCE]&order_id=[TARGET_ORDER_ID](Note: parameter names likeorder_idshould be verified viagrepin the plugin source if available).
- URL:
- Execute: Send the request using
http_request.
6. Test Data Setup
- Calculator Creation: Create a simple calculator with a total and a "Submit" or "Payment" button.
- Shortcode Placement:
wp post create --post_type=page --post_title="Payment Test" --post_status=publish --post_content='[cost_calculator id="1"]' - Order Generation: Navigate to the page, fill out the calculator, and submit it to generate a "Pending" order.
- Order ID Discovery:
# Check the database for the last created order ID and its status wp db query "SELECT id, status FROM wp_ccb_orders ORDER BY id DESC LIMIT 1"
7. Expected Results
- HTTP Response: A success code (e.g.,
200 OK) and potentially a JSON response like{"success": true}or1. - System Impact: The order status in the WordPress database for the specified
order_idtransitions frompending(or similar) tocompleted.
8. Verification Steps
After sending the AJAX request, verify the database state using WP-CLI:
# Verify the status change
wp db query "SELECT id, status FROM wp_ccb_orders WHERE id = [TARGET_ORDER_ID]"
- Success Criteria: Status is "completed" or "paid".
9. Alternative Approaches
- Parameter Guessing: If
order_idis not the correct key, tryid,order, orpayment_id. - Action String Verification: If
ccb_nonceis not the correct key in theccb_noncesobject, inspect the full object usingbrowser_eval("window.ccb_nonces")to find the relevant key. - Database Inspection: If the table name
wp_ccb_ordersis incorrect, usewp db tables | grep ccbto find the correct table name for order tracking.
Summary
The Cost Calculator Builder plugin for WordPress is vulnerable to an unauthenticated payment status bypass because it registers the complete_payment AJAX action for unauthenticated users. The handler function verifies a WordPress nonce but lacks any server-side authorization or order ownership checks, allowing attackers to manually transition orders to a completed status without payment.
Vulnerable Code
// Inferred registration of AJAX action in class constructor or init add_action( 'wp_ajax_complete_payment', array( $this, 'complete' ) ); add_action( 'wp_ajax_nopriv_complete_payment', array( $this, 'complete' ) ); // Likely handler function in a payment or order class public function complete() { // Nonce verification exists but the nonce is exposed on the frontend check_ajax_referer( 'ccb_nonce', 'nonce' ); $order_id = isset( $_POST['order_id'] ) ? intval( $_POST['order_id'] ) : 0; if ( $order_id ) { // Vulnerability: No capability check (e.g., current_user_can) // or verification that the order belongs to the current session user. $this->update_order_status( $order_id, 'completed' ); wp_send_json_success(); } }
Security Fix
@@ -1,5 +1,4 @@ add_action( 'wp_ajax_complete_payment', array( $this, 'complete' ) ); -add_action( 'wp_ajax_nopriv_complete_payment', array( $this, 'complete' ) ); public function complete() { - check_ajax_referer( 'ccb_nonce', 'nonce' ); + check_ajax_referer( 'ccb_nonce', 'nonce' ); + + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => 'Unauthorized' ) ); + return; + }
Exploit Outline
1. Locate a page on the target WordPress site that contains a Cost Calculator form (often identified by shortcodes like [cost_calculator]). 2. View the page source and inspect the global JavaScript object 'window.ccb_nonces' to extract the valid 'ccb_nonce' value. 3. Identify a target 'order_id' that is currently in a 'pending' or 'unpaid' state. 4. Send an unauthenticated HTTP POST request to '/wp-admin/admin-ajax.php' with the following parameters: action=complete_payment, nonce=[EXTRACTED_NONCE], and order_id=[TARGET_ORDER_ID]. 5. The server will process the request and update the status of the specified order to 'completed' in the database without any actual payment transaction occurring.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.