CVE-2025-12362

myCred – Points Management System For Gamification, Ranks, Badges, and Loyalty Program <= 2.9.7 - Missing Authorization to Unauthenticated Withdrawal Request Approval

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.9.7.1
Patched in
1d
Time to patch

Description

The myCred – Points Management System For Gamification, Ranks, Badges, and Loyalty Program plugin for WordPress is vulnerable to Missing Authorization in versions up to, and including, 2.9.7. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for unauthenticated attackers to approve withdrawal requests, modify user point balances, and manipulate the payment processing system via the cashcred_pay_now AJAX 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<=2.9.7
PublishedDecember 12, 2025
Last updatedDecember 13, 2025
Affected pluginmycred

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-12362 - Unauthenticated Withdrawal Approval in myCred ## 1. Vulnerability Summary The myCred plugin (up to version 2.9.7) contains a missing authorization vulnerability in its **cashCred** addon. The AJAX action `cashcred_pay_now` fails to implement proper capability check…

Show full research plan

Research Plan: CVE-2025-12362 - Unauthenticated Withdrawal Approval in myCred

1. Vulnerability Summary

The myCred plugin (up to version 2.9.7) contains a missing authorization vulnerability in its cashCred addon. The AJAX action cashcred_pay_now fails to implement proper capability checks (e.g., current_user_can( 'manage_options' )) and is improperly registered for unauthenticated users (wp_ajax_nopriv_). This allows an unauthenticated attacker to approve pending withdrawal requests, potentially triggering real-world payouts or manipulating internal point ledgers and transaction statuses.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: cashcred_pay_now
  • Vulnerable Hooks (Inferred):
    • add_action( 'wp_ajax_cashcred_pay_now', ... )
    • add_action( 'wp_ajax_nopriv_cashcred_pay_now', ... )
  • Parameters:
    • action: cashcred_pay_now
    • request_id: The ID of the pending withdrawal request.
    • _wpnonce: The nonce for the action (if required/verified).
  • Authentication: Unauthenticated (accessible via wp_ajax_nopriv).
  • Preconditions: The cashCred addon must be enabled, and at least one withdrawal request must exist in the system.

3. Code Flow (Inferred)

  1. Entry Point: An unauthenticated HTTP POST request is sent to admin-ajax.php with action=cashcred_pay_now.
  2. Handler: The request is routed to the callback function for cashcred_pay_now (likely located in addons/cashcred/includes/cashcred-functions.php or a similar class-based handler in the cashcred directory).
  3. Missing Check: The handler likely calls check_ajax_referer to verify a nonce but fails to call current_user_can().
  4. Processing: The code retrieves the withdrawal request via request_id.
  5. Sink: The plugin updates the request status to "Approved" or "Paid" and may trigger payment gateway functions (like PayPal or Stripe) or deduct/finalize point balances using $mycred->add_creds().

4. Nonce Acquisition Strategy

The cashcred_pay_now action is typically used in the admin dashboard, but since it is registered via nopriv, the nonce might be leaked on frontend pages where the cashCred shortcode or scripts are loaded.

  1. Identify Shortcode: The cashCred addon uses the [cashcred] shortcode for users to submit withdrawal requests.
  2. Create Page: Create a public page containing the shortcode.
    wp post create --post_type=page --post_status=publish --post_title="Withdraw" --post_content='[cashcred]'
    
  3. Extract Nonce: Navigate to this page and check the localized JavaScript objects.
    • Variable Name (Inferred): cashCred or mycred_cash.
    • Key: ajax_nonce or pay_now.
    • Command: browser_eval("window.cashCred?.ajax_nonce") (This must be verified by inspecting the page source for wp_localize_script calls).

Note: If the cashcred_pay_now action uses the default nonce -1 or if check_ajax_referer is used with die=false and not checked, the nonce may not be strictly required for exploitation.

5. Exploitation Strategy

  1. Capture Request ID: Identify a pending withdrawal request ID. In a test environment, this can be found via wp post list --post_type=cashcred_withdrawal (the request is likely a custom post type or a custom table entry).
  2. Obtain Nonce: Use the browser_navigate and browser_eval tools to extract the nonce from a page containing the [cashcred] shortcode.
  3. Trigger Approval: Send an unauthenticated AJAX request to "pay" (approve) the request.

Request Template:

  • Method: POST
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=cashcred_pay_now&request_id=[ID]&_wpnonce=[NONCE]
    

6. Test Data Setup

  1. Install/Enable Plugin: Ensure mycred is installed and the cashCred addon is activated in the myCred settings.
  2. Create User with Points:
    wp user create victim victim@example.com --role=subscriber
    wp eval "mycred_add('test', [USER_ID], 1000, 'Test Points');"
    
  3. Submit Withdrawal Request:
    Log in as the victim (or use wp eval) to create a cashcred_withdrawal entry for 100 points.
  4. Publish Shortcode Page:
    wp post create --post_type=page --post_status=publish --post_content='[cashcred]'
    

7. Expected Results

  • The server should respond with a JSON success message (e.g., {"success":true} or a success string).
  • The withdrawal request status should change from pending to approved or completed.
  • The user's point balance (if not deducted at submission) should be finalized.

8. Verification Steps

Check the status of the withdrawal request via WP-CLI:

# Replace [ID] with the request ID used in the exploit
wp post get [ID] --field=post_status
# Or if it's stored in meta:
wp post meta get [ID] _withdrawal_status

Verify the transaction log in the myCred database:

wp db query "SELECT * FROM wp_mycred_log ORDER BY id DESC LIMIT 5;"

9. Alternative Approaches

  • Brute-forcing Request IDs: If the ID is an incremental integer (standard for WP Posts), the attacker can iterate through recent IDs.
  • Action Name variations: If cashcred_pay_now fails, check for similar actions like cashcred-approve, mycred-gateway-pay, or cashcred_process_payment.
  • Nonce Bypass: Attempt the request without the _wpnonce parameter to check if the plugin actually enforces the check. If it uses check_ajax_referer( 'my_nonce', 'nonce', false ), the exploit will work even with an invalid nonce.
Research Findings
Static analysis — not yet PoC-verified

Summary

The myCred plugin's cashCred addon is vulnerable to missing authorization due to the improper registration of the 'cashcred_pay_now' AJAX action for unauthenticated users. Attackers can exploit this to approve pending withdrawal requests, which can lead to unauthorized financial payouts or point ledger manipulation.

Vulnerable Code

// Inferred registration in addons/cashcred/includes/cashcred-functions.php or similar
add_action( 'wp_ajax_cashcred_pay_now', 'mycred_cashcred_pay_now_handler' );
add_action( 'wp_ajax_nopriv_cashcred_pay_now', 'mycred_cashcred_pay_now_handler' );

---

// Inferred handler function lacking capability checks
function mycred_cashcred_pay_now_handler() {
    check_ajax_referer( 'mycred-cashcred-pay', '_wpnonce' );
    $request_id = intval( $_POST['request_id'] );
    // The function continues to process the payment/approval without current_user_can() verification
    // ...
}

Security Fix

--- a/addons/cashcred/includes/cashcred-functions.php
+++ b/addons/cashcred/includes/cashcred-functions.php
@@ -1,7 +1,9 @@
 add_action( 'wp_ajax_cashcred_pay_now', 'mycred_cashcred_pay_now_handler' );
-add_action( 'wp_ajax_nopriv_cashcred_pay_now', 'mycred_cashcred_pay_now_handler' );
 
 function mycred_cashcred_pay_now_handler() {
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( 'Unauthorized' );
+    }
     check_ajax_referer( 'mycred-cashcred-pay', '_wpnonce' );

Exploit Outline

The exploit involves an unauthenticated attacker triggering the 'cashcred_pay_now' AJAX action. First, the attacker identifies a page on the target site where the '[cashcred]' shortcode is used, which typically leaks the required security nonce via localized JavaScript objects (e.g., window.cashCred.ajax_nonce). The attacker then identifies a target withdrawal request ID, which may be discoverable or brute-forced if numeric. Finally, the attacker sends an unauthenticated POST request to /wp-admin/admin-ajax.php with the action set to 'cashcred_pay_now', the target 'request_id', and the leaked nonce. Because the plugin uses wp_ajax_nopriv and fails to verify user capabilities, the withdrawal is approved and processed as if by an administrator.

Check if your site is affected.

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