CVE-2025-49340

Direct Payments WP <= 1.3.0 - Authenticated (Subscriber+) Sensitive Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Direct Payments WP plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 1.3.0. This makes it possible for authenticated attackers, with Subscriber-level access and above, to extract sensitive user or configuration data.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=1.3.0
PublishedDecember 31, 2025
Last updatedJanuary 5, 2026
Affected plugindirect-payments-wp
Research Plan
Unverified

Since the source code for **Direct Payments WP <= 1.3.0** is not provided, this research plan is based on the vulnerability description and common patterns for "Authenticated Sensitive Information Exposure" in WordPress payment plugins. ### 1. Vulnerability Summary The **Direct Payments WP** plugin…

Show full research plan

Since the source code for Direct Payments WP <= 1.3.0 is not provided, this research plan is based on the vulnerability description and common patterns for "Authenticated Sensitive Information Exposure" in WordPress payment plugins.

1. Vulnerability Summary

The Direct Payments WP plugin fails to implement proper authorization checks (capability checks) on one or more AJAX handlers or REST API endpoints. This allows an authenticated user with minimal privileges (Subscriber) to trigger functions intended for administrators. These functions return sensitive data such as plugin configuration settings (potentially containing API keys, merchant IDs, or secret tokens) or user-related metadata.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php (Most likely) or a REST API route under /wp-json/.
  • Vulnerable Action: Likely prefixed with dpwp_ or direct_payments_. (e.g., dpwp_get_settings, direct_payments_get_log, dpwp_export_config).
  • Authentication: Subscriber level or higher (PR:L).
  • Parameter: The action parameter in a POST request to admin-ajax.php.
  • Precondition: A valid Subscriber account and a valid AJAX nonce if the plugin verifies one.

3. Code Flow (Inferred)

  1. Registration: The plugin registers an AJAX action via add_action( 'wp_ajax_...', ... ).
  2. Missing Check: The callback function associated with this action fails to call current_user_can( 'manage_options' ).
  3. Data Retrieval: The function retrieves sensitive values using get_option( 'dpwp_settings' ) or similar.
  4. Information Leak: The function outputs the data using wp_send_json() or echo json_encode(), which is returned in the HTTP response body.

4. Nonce Acquisition Strategy

If the endpoint requires a nonce (which is common for authenticated AJAX), we must identify where it is leaked.

  1. Identify Entry Point: Use grep -r "wp_localize_script" . to find where the plugin passes data to JavaScript.
  2. Find Nonce Key: Look for a key named nonce, ajax_nonce, or _wpnonce within the localized data object.
    • Example Localization: wp_localize_script( 'dpwp-script', 'dpwp_vars', array( 'nonce' => wp_create_nonce('dpwp_ajax_action') ) );
  3. Find Shortcode: Search for add_shortcode to see which shortcode enqueues the script (e.g., [direct-payments-wp]).
  4. Extraction Process:
    • Step A: Create a dummy page containing the identified shortcode:
      wp post create --post_type=page --post_status=publish --post_content='[SHORTCODE_NAME]'
    • Step B: Navigate to this page as the Subscriber user using browser_navigate.
    • Step C: Extract the nonce:
      browser_eval("window.dpwp_vars?.nonce || window.direct_payments_obj?.nonce") (Replace variable names based on grep results).

5. Exploitation Strategy

Once the action name and nonce are identified, follow these steps:

  1. Preparation:
    • Authenticate as a Subscriber using the http_request tool to get cookies.
  2. Request Construction:
    • URL: http://<target>/wp-admin/admin-ajax.php
    • Method: POST
    • Content-Type: application/x-www-form-urlencoded
    • Body: action=<VULNERABLE_ACTION>&security=<NONCE>&other_params=...
  3. Discovery (Automated Agent):
    • The agent should first grep the plugin directory for wp_ajax_ to find all registered actions.
    • Filter actions that lack current_user_can in their callback but do have get_option or database queries.
  4. Payload Execution:
    # Example Request using the agent's tool
    http_request POST "http://localhost:8080/wp-admin/admin-ajax.php" \
      --data "action=dpwp_get_settings&security=1234567890" \
      --cookie "wordpress_logged_in_..."
    

6. Test Data Setup

  1. Plugin Setup: Install and activate direct-payments-wp version 1.3.0.
  2. Settings Initialization: Add dummy sensitive data to the plugin settings (e.g., a fake Stripe Secret Key sk_test_4eC39HqLyjWDarjtT1zdp7dc) via the admin panel.
  3. User Creation:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
  4. Page Setup:
    wp post create --post_type=page --post_title="Payment" --post_status=publish --post_content='[direct-payments-wp]' (Verify the exact shortcode via grep).

7. Expected Results

  • Success: The HTTP response returns a 200 OK with a JSON body containing the plugin's configuration, including the dummy Stripe Key or other sensitive merchant settings.
  • Vulnerable Response Example:
    {
      "success": true,
      "data": {
        "stripe_api_key": "sk_test_4eC39HqLyjWDarjtT1zdp7dc",
        "merchant_email": "admin@target.com",
        "payment_mode": "test"
      }
    }
    

8. Verification Steps

  1. Compare with Database: Run wp option get <OPTION_NAME> (e.g., dpwp_settings) via CLI.
  2. Confirm Leak: Verify that the data returned in the AJAX response matches the sensitive data stored in the database.
  3. Privilege Confirmation: Ensure the request was made with the Subscriber user's cookies and not an Administrator's.

9. Alternative Approaches

  • REST API Check: If no AJAX actions seem vulnerable, check for REST routes:
    grep -r "register_rest_route" .
    Inspect the permission_callback. If it uses __return_true or only checks is_user_logged_in(), it is vulnerable.
  • Action Brute-force: If grep is restricted, common names like get_settings, settings_load, fetch_config should be tested with the dpwp_ and direct_payments_wp_ prefixes.
  • No-Nonce Test: Some plugins check for a nonce but don't die() if it's missing (using check_ajax_referer with the third argument as false). Test the request without the security parameter.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Direct Payments WP plugin for WordPress (up to version 1.3.0) fails to restrict access to sensitive configuration data through its AJAX interface. Authenticated users with Subscriber-level privileges can invoke internal functions that return plugin settings, potentially exposing payment gateway API keys, merchant identifiers, and secret tokens.

Vulnerable Code

// direct-payments-wp/includes/class-ajax-handler.php (Inferred Location)
add_action( 'wp_ajax_dpwp_get_settings', 'dpwp_get_settings_callback' );

function dpwp_get_settings_callback() {
    // Vulnerability: No check for user capabilities (e.g., current_user_can('manage_options'))
    // and potentially missing or weak nonce verification.
    $settings = get_option( 'dpwp_settings' );
    wp_send_json_success( $settings );
}

Security Fix

--- direct-payments-wp/includes/class-ajax-handler.php
+++ direct-payments-wp/includes/class-ajax-handler.php
@@ -2,6 +2,11 @@
 function dpwp_get_settings_callback() {
+    check_ajax_referer( 'dpwp_ajax_nonce', 'security' );
+
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( 'Unauthorized', 403 );
+    }
+
     $settings = get_option( 'dpwp_settings' );
     wp_send_json_success( $settings );
 }

Exploit Outline

1. Authentication: Authenticate to the WordPress site as a user with Subscriber-level privileges. 2. Nonce Acquisition: Access a public or subscriber-accessible page containing the plugin's shortcode. Locate the localized JavaScript object (e.g., via `dpwp_vars` or similar) to extract the valid AJAX nonce. 3. Request Execution: Send a POST request to `/wp-admin/admin-ajax.php` with the vulnerable action (e.g., `action=dpwp_get_settings`) and the retrieved nonce in the security parameter. 4. Data Extraction: The response body will contain the full plugin configuration in JSON format, which includes sensitive credentials such as Stripe Secret Keys or merchant configuration details.

Check if your site is affected.

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